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-default}
15 ref=x
16 echo x=${!ref-default}
17
18 x=''
19 echo x=${!ref-default}
20
21 x=foo
22 echo x=${!ref-default}
23
24 ## STDOUT:
25 x=default
26 x=
27 x=foo
28 ## END
29
30 #### ${!undef:-}
31 # bash gives empty string, but I feel like this could be an error
32 echo undef=${!undef-'default'}
33 echo undef=${!undef}
34
35 set -u
36 echo NOUNSET
37 echo undef=${!undef-'default'}
38 echo undef=${!undef}
39
40 ## status: 1
41 ## STDOUT:
42 undef=default
43 undef=
44 NOUNSET
45 undef=default
46 ## END
47
48 #### comparison to ${!array[@]} keys (similar SYNTAX)
49
50 declare -a a=(x y)
51 argv.py "${!a[@]}"
52 echo a_keys=$?
53
54 argv.py "${!a}" # missing [] is equivalent to ${!a[0]} ?
55 echo a_nobrackets=$?
56
57 echo ---
58 declare -A A=([A]=a [B]=b)
59
60 argv.py ${!A[@]}
61 echo A_keys=$?
62
63 argv.py "${!A}" # missing [] is equivalent to ${!A[0]} ?
64 echo A_nobrackets=$?
65
66 ## STDOUT:
67 ['0', '1']
68 a_keys=0
69 ['']
70 a_nobrackets=0
71 ---
72 ['A', 'B']
73 A_keys=0
74 ['']
75 A_nobrackets=0
76 ## END
77
78 #### ${!a[@]-'default'} is illegal
79
80 # bash disallows this when a is an array. We make it an error because [@]
81 # implies it's an array.
82
83 argv.py "${!a[@]-default}"
84 echo status=$?
85
86 a=(x y z)
87 argv.py "${!a[@]-default}"
88 echo status=$?
89 ## status: 1
90 ## STDOUT:
91 ## END
92 ## BUG bash status: 0
93 ## BUG bash STDOUT:
94 ['default']
95 status=0
96 status=1
97 ## END
98
99
100 #### var ref to $@ with @
101 set -- one two
102 ref='@'
103 echo ref=${!ref}
104 ## STDOUT:
105 ref=one two
106 ## END
107
108 #### var ref to $1 and $2 with 1 and 2
109 set -- one two
110 ref1='1'
111 echo ref1=${!ref1}
112 ref2='2'
113 echo ref2=${!ref2}
114
115 ## STDOUT:
116 ref1=one
117 ref2=two
118 ## END
119
120 #### var ref: 1, @, *
121 set -- x y
122 ref=1; argv.py "${!ref}"
123 ref=@; argv.py "${!ref}"
124 ref=*; argv.py "${!ref}" # maybe_decay_array bug?
125
126 ## STDOUT:
127 ['x']
128 ['x', 'y']
129 ['x y']
130 ## END
131
132 #### var ref to special var BASH_SOURCE
133 ref='LINENO'
134 echo lineno=${!ref}
135 ## STDOUT:
136 lineno=2
137 ## END
138
139 #### var ref to $? with '?'
140 myfunc() {
141 local ref=$1
142 echo ${!ref}
143 }
144 myfunc FUNCNAME
145 myfunc '?'
146 ## STDOUT:
147 myfunc
148 0
149 ## END
150
151
152 #### Var ref, then assignment with ${ := }
153 z=zz
154 zz=
155 echo ${!z:=foo}
156 echo ${!z:=bar}
157 ## STDOUT:
158 foo
159 foo
160 ## END
161
162 #### Var ref, then error with ${ ? }
163 w=ww
164 ww=
165 echo ${!w:?'my message'}
166 echo done
167 ## status: 1
168 ## STDOUT:
169 ## END
170
171 #### Indirect expansion, THEN suffix operators
172
173 check_eq() {
174 [ "$1" = "$2" ] || { echo "$1 vs $2"; }
175 }
176 check_expand() {
177 val=$(eval "echo \"$1\"")
178 [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; }
179 }
180 check_err() {
181 e="$1"
182 msg=$(eval "$e" 2>&1) && echo "bad success: $e"
183 if test -n "$2"; then
184 if [[ "$msg" != $2 ]]; then
185 echo "Expected error: $e"
186 echo "Got error : $msg"
187 fi
188 fi
189 }
190 # Nearly everything in manual section 3.5.3 "Shell Parameter Expansion"
191 # is allowed after a !-indirection.
192 #
193 # Not allowed: any further prefix syntax.
194 x=xx; xx=aaabcc
195 xd=x
196 check_err '${!!xd}'
197 check_err '${!!x*}'
198 a=(asdf x)
199 check_err '${!!a[*]}'
200 check_err '${!#x}'
201 check_err '${!#a[@]}'
202 # And an array reference binds tighter in the syntax, so goes first;
203 # there's no way to spell "indirection, then array reference".
204 check_expand '${!a[1]}' xx
205 b=(aoeu a)
206 check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1]
207 #
208 # Allowed: apparently everything else.
209 y=yy; yy=
210 check_expand '${!y:-foo}' foo
211 check_expand '${!x:-foo}' aaabcc
212
213 check_expand '${!x:?oops}' aaabcc
214
215 check_expand '${!y:+foo}' ''
216 check_expand '${!x:+foo}' foo
217
218 check_expand '${!x:2}' abcc
219 check_expand '${!x:2:2}' ab
220
221 check_expand '${!x#*a}' aabcc
222 check_expand '${!x%%c*}' aaab
223 check_expand '${!x/a*b/d}' dcc
224
225 # ^ operator not fully implemented in OSH
226 #check_expand '${!x^a}' Aaabcc
227
228 p=pp; pp='\$ '
229 check_expand '${!p@P}' '$ '
230 echo ok
231 ## stdout: ok
232
233 #### var ref OF array var -- silent a[0] decay
234 declare -a a=(ale bean)
235 echo first=${!a}
236
237 ale=zzz
238 echo first=${!a}
239
240 ## status: 0
241 ## STDOUT:
242 first=
243 first=zzz
244 ## END
245
246 #### array ref
247
248 declare -a array=(ale bean)
249 ref='array[0]'
250 echo ${!ref}
251 ## status: 0
252 ## STDOUT:
253 ale
254 ## END
255
256 #### array ref with strict_array
257 shopt -s strict_array
258
259 declare -a array=(ale bean)
260 ref='array'
261 echo ${!ref}
262 ## status: 1
263 ## stdout-json: ""
264 ## N-I bash status: 0
265 ## N-I bash STDOUT:
266 ale
267 ## END
268
269 #### var ref TO array var
270 shopt -s compat_array
271
272 declare -a array=(ale bean)
273
274 ref='array' # when compat_array is on, this is like array[0]
275 ref_AT='array[@]'
276
277 echo ${!ref}
278 echo ${!ref_AT}
279
280 ## STDOUT:
281 ale
282 ale bean
283 ## END
284
285 #### var ref TO array var, with subscripts
286 f() {
287 argv.py "${!1}"
288 }
289 f 'nonexistent[0]'
290 array=(x y z)
291 f 'array[0]'
292 f 'array[1+1]'
293 f 'array[@]'
294 f 'array[*]'
295 # Also associative arrays.
296 ## STDOUT:
297 ['']
298 ['x']
299 ['z']
300 ['x', 'y', 'z']
301 ['x y z']
302 ## END
303
304 #### var ref TO assoc array a[key]
305 shopt -s compat_array
306
307 declare -A assoc=([ale]=bean [corn]=dip)
308 ref=assoc
309 #ref_AT='assoc[@]'
310
311 # UNQUOTED doesn't work with Oil's parser
312 #ref_SUB='assoc[ale]'
313 ref_SUB='assoc["ale"]'
314
315 ref_SUB_QUOTED='assoc["al"e]'
316
317 ref_SUB_BAD='assoc["bad"]'
318
319 echo ref=${!ref} # compat_array: assoc is equivalent to assoc[0]
320 #echo ref_AT=${!ref_AT}
321 echo ref_SUB=${!ref_SUB}
322 echo ref_SUB_QUOTED=${!ref_SUB_QUOTED}
323 echo ref_SUB_BAD=${!ref_SUB_BAD}
324
325 ## STDOUT:
326 ref=
327 ref_SUB=bean
328 ref_SUB_QUOTED=bean
329 ref_SUB_BAD=
330 ## END
331
332 #### var ref TO array with arbitrary subscripts
333 shopt -s eval_unsafe_arith compat_array
334
335 f() {
336 local val=$(echo "${!1}")
337 if test "$val" = y; then
338 echo "works: $1"
339 fi
340 }
341 # Warmup: nice plain array reference
342 a=(x y)
343 f 'a[1]'
344 #
345 # Not allowed:
346 # no brace expansion
347 f 'a[{1,0}]' # operand expected
348 # no process substitution (but see command substitution below!)
349 f 'a[<(echo x)]' # operand expected
350 # TODO word splitting seems interesting
351 aa="1 0"
352 f 'a[$aa]' # 1 0: syntax error in expression (error token is "0")
353 # no filename globbing
354 f 'a[b*]' # operand expected
355 f 'a[1"]' # bad substitution
356 #
357 # Allowed: most everything else in section 3.5 "Shell Expansions".
358 # shell parameter expansion
359 b=1
360 f 'a[$b]'
361 f 'a[${c:-1}]'
362 # (... and presumably most of the other features there)
363 # command substitution, yikes!
364 f 'a[$(echo 1)]'
365 # arithmetic expansion
366 f 'a[$(( 3 - 2 ))]'
367
368 # All of these are undocumented and probably shouldn't exist,
369 # though it's always possible some will turn up in the wild and
370 # we'll end up implementing them.
371
372 ## STDOUT:
373 works: a[1]
374 works: a[$b]
375 works: a[${c:-1}]
376 works: a[$(echo 1)]
377 works: a[$(( 3 - 2 ))]
378 ## END
379
380 #### Bizarre tilde expansion in array index
381 a=(x y)
382 PWD=1
383 ref='a[~+]'
384 echo ${!ref}
385 ## status: 1
386 ## BUG bash status: 0
387 ## BUG bash STDOUT:
388 y
389 ## END
390
391 #### Indirect expansion TO fancy expansion features bash disallows
392
393 check_indir() {
394 result="${!1}"
395 desugared_result=$(eval 'echo "${'"$1"'}"')
396 [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; }
397 }
398 x=y
399 y=a
400 a=(x y)
401 declare -A aa
402 aa=([k]=r [l]=s)
403 # malformed array indexing
404 check_indir "a[0"
405 check_indir "aa[k"
406 # double indirection
407 check_indir "!x" a
408 check_indir "!a[0]" y
409 # apparently everything else in the manual under "Shell Parameter Expansion"
410 check_indir "x:-foo" y
411 check_indir "x:=foo" y
412 check_indir "x:?oops" y
413 check_indir "x:+yy" yy
414 check_indir "x:0" y
415 check_indir "x:0:1" y
416 check_indir "!a@" "a aa"
417 # (!a[@] is elsewhere)
418 check_indir "#x" 1
419 check_indir "x#y"
420 check_indir "x/y/foo" foo
421 check_indir "x@Q" "'y'"
422 echo done
423 ## status: 1
424 ## stdout-json: ""
425 ## OK bash status: 0
426 ## OK bash stdout: done
427
428 #### Bad var ref
429 a='bad var name'
430 echo ref ${!a}
431 echo status=$?
432
433 ## STDOUT:
434 status=1
435 ## END
436 ## OK osh stdout-json: ""
437 ## OK osh status: 1
438
439 #### Bad var ref 2
440 b='/' # really bad
441 echo ref ${!b}
442 echo status=$?
443 ## STDOUT:
444 status=1
445 ## END
446 ## OK osh stdout-json: ""
447 ## OK osh status: 1
448
449 #### ${!OPTIND} (used by bash completion
450 set -- a b c
451 echo ${!OPTIND}
452 f() {
453 local OPTIND=1
454 echo ${!OPTIND}
455 local OPTIND=2
456 echo ${!OPTIND}
457 }
458 f x y z
459 ## STDOUT:
460 a
461 x
462 y
463 ## END
464
465 #### var ref doesn't need cycle detection
466 x=y
467 y=x
468 echo cycle=${!x}
469
470 typeset -n a=b
471 typeset -n b=a
472 echo cycle=${a}
473 ## status: 1
474 ## STDOUT:
475 cycle=x
476 ## END
477 ## OK bash status: 0
478 ## OK bash STDOUT:
479 cycle=x
480 cycle=
481 ## END
482
483 #### Var Ref Code Injection $(tee PWNED)
484
485 typeset -a a
486 a=(42)
487
488 x='a[$(echo 0 | tee PWNED)]'
489
490 echo ${!x}
491
492 if test -f PWNED; then
493 echo PWNED
494 cat PWNED
495 else
496 echo NOPE
497 fi
498
499 ## status: 1
500 ## STDOUT:
501 ## END
502
503 ## BUG bash status: 0
504 ## BUG bash STDOUT:
505 42
506 PWNED
507 0
508 ## END