OILS / spec / var-ref.test.sh View on Github | oilshell.org

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