1 #
2 # Var refs are done with ${!a}
3 #
4 # local/declare -n is tested in spec/named-ref.test.sh.
5 #
6 # http://stackoverflow.com/questions/16461656/bash-how-to-pass-array-as-an-argument-to-a-function
7
8 #### var ref ${!a}
9 a=b
10 b=c
11 echo ref ${!a} ${a}
12 ## stdout: ref c b
13
14 #### ref to $@ with @
15 set -- one two
16 ref='@'
17 echo ref=${!ref}
18 ## STDOUT:
19 ref=one two
20 ## END
21
22 #### ref to $1 and $2 with 1 and 2
23 set -- one two
24 ref1='1'
25 echo ref1=${!ref1}
26 ref2='2'
27 echo ref2=${!ref2}
28
29 ## STDOUT:
30 ref1=one
31 ref2=two
32 ## END
33
34 #### var ref with 1 and @ and *
35 set -- x y
36 ref=1; printf "|%s" "${!ref}" $'\n'
37 ref=@; printf "|%s" "${!ref}" $'\n'
38 ref=*; printf "|%s" "${!ref}" $'\n'
39 ## STDOUT:
40 |x|
41 |x|y|
42 |x y|
43 ## END
44
45 #### var ref to special var BASH_SOURCE
46 ref='LINENO'
47 echo lineno=${!ref}
48 ## STDOUT:
49 lineno=2
50 ## END
51
52 #### var ref to $? with '?' (not in Oil)
53 myfunc() {
54 local ref=$1
55 echo ${!ref}
56 }
57 myfunc FUNCNAME
58 myfunc '?' # osh doesn't do this dynamically
59 ## STDOUT:
60 myfunc
61 0
62 ## END
63 ## N-I osh STDOUT:
64 myfunc
65 ## END
66 ## N-I osh status: 1
67
68 #### indirection, *then* fancy expansion features
69 check_eq() {
70 [ "$1" = "$2" ] || { echo "$1 vs $2"; }
71 }
72 check_expand() {
73 val=$(eval "echo \"$1\"")
74 [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; }
75 }
76 check_err() {
77 e="$1"
78 msg=$(eval "$e" 2>&1) && echo "bad success: $e"
79 [ -z "$2" ] || [[ "$msg" == $2 ]] || echo "bad err msg: $e -> $msg"
80 }
81 # Nearly everything in manual section 3.5.3 "Shell Parameter Expansion"
82 # is allowed after a !-indirection.
83 #
84 # Not allowed: any further prefix syntax.
85 x=xx; xx=aaabcc
86 xd=x
87 check_err '${!!xd}'
88 check_err '${!!x*}'
89 a=(asdf x)
90 check_err '${!!a[*]}'
91 check_err '${!#x}'
92 check_err '${!#a[@]}'
93 # And an array reference binds tighter in the syntax, so goes first;
94 # there's no way to spell "indirection, then array reference".
95 check_expand '${!a[1]}' xx
96 b=(aoeu a)
97 check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1]
98 #
99 # Allowed: apparently everything else.
100 y=yy; yy=
101 check_expand '${!y:-foo}' foo
102 check_expand '${!x:-foo}' aaabcc
103 z=zz; zz=
104 check_eq "${!z:=foo}" foo ; check_expand '$zz' foo
105 check_eq "${!z:=bar}" foo ; check_expand '$zz' foo
106 w=ww; ww=
107 check_err '${!w:?oops}' '*: oops'
108 check_expand '${!x:?oops}' aaabcc
109 check_expand '${!y:+foo}' ''
110 check_expand '${!x:+foo}' foo
111 check_expand '${!x:2}' abcc
112 check_expand '${!x:2:2}' ab
113 check_expand '${!x#*a}' aabcc
114 check_expand '${!x%%c*}' aaab
115 check_expand '${!x/a*b/d}' dcc
116 check_expand '${!x^a}' Aaabcc
117 p=pp; pp='\$ '
118 check_expand '${!p@P}' '$ '
119 echo ok
120 ## stdout: ok
121
122 #### indirection *to* an array reference
123 f() {
124 printf ".%s" "${!1}"
125 echo
126 }
127 f a[0]
128 b=(x y)
129 f b[0]
130 f b[@]
131 f "b[*]"
132 # Also associative arrays.
133 ## STDOUT:
134 .
135 .x
136 .x.y
137 .x y
138 ## END
139
140 #### indirection to nasty complex array references
141 i=0
142 f() {
143 ((i++))
144 val=$(echo "${!1}")
145 [ "$val" = y ] && echo -n "$i "
146 }
147 # Warmup: nice plain array reference
148 a=(x y)
149 f 'a[1]'
150 #
151 # Not allowed:
152 # no brace expansion
153 f 'a[{1,0}]' # operand expected
154 # no process substitution (but see command substitution below!)
155 f 'a[<(echo x)]' # operand expected
156 # TODO word splitting seems interesting
157 aa="1 0"
158 f 'a[$aa]' # 1 0: syntax error in expression (error token is "0")
159 # no filename globbing
160 f 'a[b*]' # operand expected
161 f 'a[1"]' # bad substitution
162 #
163 # Allowed: most everything else in section 3.5 "Shell Expansions".
164 # tilde expansion
165 ( PWD=1; f 'a[~+]' ); ((i++))
166 # shell parameter expansion
167 b=1
168 f 'a[$b]'
169 f 'a[${c:-1}]'
170 # (... and presumably most of the other features there)
171 # command substitution, yikes!
172 f 'a[$(echo 1)]'
173 # arithmetic expansion
174 f 'a[$(( 3 - 2 ))]'
175 echo end
176 # All of these are undocumented and probably shouldn't exist,
177 # though it's always possible some will turn up in the wild and
178 # we'll end up implementing them.
179 ## stdout: 1 end
180 ## OK bash stdout: 1 7 8 9 10 11 end
181
182 #### indirection *to* fancy expansion features bash disallows
183 check_indir() {
184 result="${!1}"
185 desugared_result=$(eval 'echo "${'"$1"'}"')
186 [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; }
187 }
188 x=y
189 y=a
190 a=(x y)
191 declare -A aa
192 aa=([k]=r [l]=s)
193 # malformed array indexing
194 check_indir "a[0"
195 check_indir "aa[k"
196 # double indirection
197 check_indir "!x" a
198 check_indir "!a[0]" y
199 # apparently everything else in the manual under "Shell Parameter Expansion"
200 check_indir "x:-foo" y
201 check_indir "x:=foo" y
202 check_indir "x:?oops" y
203 check_indir "x:+yy" yy
204 check_indir "x:0" y
205 check_indir "x:0:1" y
206 check_indir "!a@" "a aa"
207 # (!a[@] is elsewhere)
208 check_indir "#x" 1
209 check_indir "x#y"
210 check_indir "x/y/foo" foo
211 check_indir "x@Q" "'y'"
212 echo done
213 ## status: 1
214 ## stdout-json: ""
215 ## OK bash status: 0
216 ## OK bash stdout: done
217
218 #### Bad var ref with ${!a}
219 a='bad var name'
220 echo ref ${!a}
221 echo status=$?
222 ## STDOUT:
223 status=1
224 ## END
225
226 # this error is fatal in osh
227 ## OK osh stdout-json: ""
228 ## OK osh status: 1
229
230 #### ${!OPTIND} (used by bash completion
231 set -- a b c
232 echo ${!OPTIND}
233 f() {
234 local OPTIND=1
235 echo ${!OPTIND}
236 local OPTIND=2
237 echo ${!OPTIND}
238 }
239 f x y z
240 ## STDOUT:
241 a
242 x
243 y
244 ## END
245
246 #### ${!ref-default}
247
248 ref=x
249
250 echo x=${!ref-default}
251
252 x=''
253 echo x=${!ref-default}
254
255 x=foo
256 echo x=${!ref-default}
257
258 ## STDOUT:
259 x=default
260 x=
261 x=foo
262 ## END
263