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

770 lines, 488 significant
1## compare_shells: bash-4.4
2## oils_failures_allowed: 1
3
4
5# Var refs are done with ${!a}
6#
7# local/declare -n is tested in spec/named-ref.test.sh.
8#
9# http://stackoverflow.com/questions/16461656/bash-how-to-pass-array-as-an-argument-to-a-function
10
11#### var ref ${!a}
12a=b
13b=c
14echo ref ${!a} ${a}
15## stdout: ref c b
16
17#### ${!ref-default}
18ref=x
19echo x=${!ref-default}
20
21x=''
22echo x=${!ref-default}
23
24x=foo
25echo x=${!ref-default}
26
27## STDOUT:
28x=default
29x=
30x=foo
31## END
32
33#### ${!undef:-}
34# bash gives empty string, but I feel like this could be an error
35echo undef=${!undef-'default'}
36echo undef=${!undef}
37
38set -u
39echo NOUNSET
40echo undef=${!undef-'default'}
41echo undef=${!undef}
42
43## status: 1
44## STDOUT:
45undef=default
46undef=
47NOUNSET
48undef=default
49## END
50
51#### comparison to ${!array[@]} keys (similar SYNTAX)
52
53declare -a a=(x y)
54argv.py "${!a[@]}"
55echo a_keys=$?
56
57argv.py "${!a}" # missing [] is equivalent to ${!a[0]} ?
58echo a_nobrackets=$?
59
60echo ---
61declare -A A=([A]=a [B]=b)
62
63argv.py ${!A[@]}
64echo A_keys=$?
65
66argv.py "${!A}" # missing [] is equivalent to ${!A[0]} ?
67echo A_nobrackets=$?
68
69## STDOUT:
70['0', '1']
71a_keys=0
72['']
73a_nobrackets=0
74---
75['A', 'B']
76A_keys=0
77['']
78A_nobrackets=0
79## END
80
81#### ${!a[@]-'default'} is illegal
82
83# bash disallows this when a is an array. We originally made it an error
84# because [@] implies it's an array, but we now changed to follow Bash when the
85# array has a single element.
86
87argv.py "${!a[@]-default}"
88echo status=$?
89
90a=(x y z)
91argv.py "${!a[@]-default}"
92echo status=$?
93## status: 1
94## STDOUT:
95['default']
96status=0
97## END
98## BUG bash status: 0
99## BUG bash STDOUT:
100['default']
101status=0
102status=1
103## END
104
105
106#### var ref to $@ with @
107set -- one two
108ref='@'
109echo ref=${!ref}
110## STDOUT:
111ref=one two
112## END
113
114#### var ref to $1 and $2 with 1 and 2
115set -- one two
116ref1='1'
117echo ref1=${!ref1}
118ref2='2'
119echo ref2=${!ref2}
120
121## STDOUT:
122ref1=one
123ref2=two
124## END
125
126#### var ref: 1, @, *
127set -- x y
128ref=1; argv.py "${!ref}"
129ref=@; argv.py "${!ref}"
130ref=*; argv.py "${!ref}" # maybe_decay_array bug?
131
132## STDOUT:
133['x']
134['x', 'y']
135['x y']
136## END
137
138#### var ref to special var BASH_SOURCE
139ref='LINENO'
140echo lineno=${!ref}
141## STDOUT:
142lineno=2
143## END
144
145#### var ref to $? with '?'
146myfunc() {
147 local ref=$1
148 echo ${!ref}
149}
150myfunc FUNCNAME
151myfunc '?'
152## STDOUT:
153myfunc
1540
155## END
156
157
158#### Var ref, then assignment with ${ := }
159z=zz
160zz=
161echo ${!z:=foo}
162echo ${!z:=bar}
163## STDOUT:
164foo
165foo
166## END
167
168#### Var ref, then error with ${ ? }
169w=ww
170ww=
171echo ${!w:?'my message'}
172echo done
173## status: 1
174## STDOUT:
175## END
176
177#### Indirect expansion, THEN suffix operators
178
179check_eq() {
180 [ "$1" = "$2" ] || { echo "$1 vs $2"; }
181}
182check_expand() {
183 val=$(eval "echo \"$1\"")
184 [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; }
185}
186check_err() {
187 e="$1"
188 msg=$(eval "$e" 2>&1) && echo "bad success: $e"
189 if test -n "$2"; then
190 if [[ "$msg" != $2 ]]; then
191 echo "Expected error: $e"
192 echo "Got error : $msg"
193 fi
194 fi
195}
196# Nearly everything in manual section 3.5.3 "Shell Parameter Expansion"
197# is allowed after a !-indirection.
198#
199# Not allowed: any further prefix syntax.
200x=xx; xx=aaabcc
201xd=x
202check_err '${!!xd}'
203check_err '${!!x*}'
204a=(asdf x)
205check_err '${!!a[*]}'
206check_err '${!#x}'
207check_err '${!#a[@]}'
208# And an array reference binds tighter in the syntax, so goes first;
209# there's no way to spell "indirection, then array reference".
210check_expand '${!a[1]}' xx
211b=(aoeu a)
212check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1]
213#
214# Allowed: apparently everything else.
215y=yy; yy=
216check_expand '${!y:-foo}' foo
217check_expand '${!x:-foo}' aaabcc
218
219check_expand '${!x:?oops}' aaabcc
220
221check_expand '${!y:+foo}' ''
222check_expand '${!x:+foo}' foo
223
224check_expand '${!x:2}' abcc
225check_expand '${!x:2:2}' ab
226
227check_expand '${!x#*a}' aabcc
228check_expand '${!x%%c*}' aaab
229check_expand '${!x/a*b/d}' dcc
230
231# ^ operator not fully implemented in OSH
232#check_expand '${!x^a}' Aaabcc
233
234p=pp; pp='\$ '
235check_expand '${!p@P}' '$ '
236echo ok
237## stdout: ok
238
239#### var ref OF array var -- silent a[0] decay
240declare -a a=(ale bean)
241echo first=${!a}
242
243ale=zzz
244echo first=${!a}
245
246## status: 0
247## STDOUT:
248first=
249first=zzz
250## END
251
252#### array ref
253
254declare -a array=(ale bean)
255ref='array[0]'
256echo ${!ref}
257## status: 0
258## STDOUT:
259ale
260## END
261
262#### array ref with strict_array
263shopt -s strict_array
264
265declare -a array=(ale bean)
266ref='array'
267echo ${!ref}
268## status: 1
269## stdout-json: ""
270## N-I bash status: 0
271## N-I bash STDOUT:
272ale
273## END
274
275#### var ref TO array var
276shopt -s compat_array
277
278declare -a array=(ale bean)
279
280ref='array' # when compat_array is on, this is like array[0]
281ref_AT='array[@]'
282
283echo ${!ref}
284echo ${!ref_AT}
285
286## STDOUT:
287ale
288ale bean
289## END
290
291#### var ref TO array var, with subscripts
292f() {
293 argv.py "${!1}"
294}
295f 'nonexistent[0]'
296array=(x y z)
297f 'array[0]'
298f 'array[1+1]'
299f 'array[@]'
300f 'array[*]'
301# Also associative arrays.
302## STDOUT:
303['']
304['x']
305['z']
306['x', 'y', 'z']
307['x y z']
308## END
309
310#### var ref TO assoc array a[key]
311shopt -s compat_array
312
313declare -A assoc=([ale]=bean [corn]=dip)
314ref=assoc
315#ref_AT='assoc[@]'
316
317# UNQUOTED doesn't work with Oil's parser
318#ref_SUB='assoc[ale]'
319ref_SUB='assoc["ale"]'
320
321ref_SUB_QUOTED='assoc["al"e]'
322
323ref_SUB_BAD='assoc["bad"]'
324
325echo ref=${!ref} # compat_array: assoc is equivalent to assoc[0]
326#echo ref_AT=${!ref_AT}
327echo ref_SUB=${!ref_SUB}
328echo ref_SUB_QUOTED=${!ref_SUB_QUOTED}
329echo ref_SUB_BAD=${!ref_SUB_BAD}
330
331## STDOUT:
332ref=
333ref_SUB=bean
334ref_SUB_QUOTED=bean
335ref_SUB_BAD=
336## END
337
338#### var ref TO array with arbitrary subscripts
339shopt -s eval_unsafe_arith compat_array
340
341f() {
342 local val=$(echo "${!1}")
343 if test "$val" = y; then
344 echo "works: $1"
345 fi
346}
347# Warmup: nice plain array reference
348a=(x y)
349f 'a[1]'
350#
351# Not allowed:
352# no brace expansion
353f 'a[{1,0}]' # operand expected
354# no process substitution (but see command substitution below!)
355f 'a[<(echo x)]' # operand expected
356# TODO word splitting seems interesting
357aa="1 0"
358f 'a[$aa]' # 1 0: syntax error in expression (error token is "0")
359# no filename globbing
360f 'a[b*]' # operand expected
361f 'a[1"]' # bad substitution
362#
363# Allowed: most everything else in section 3.5 "Shell Expansions".
364# shell parameter expansion
365b=1
366f 'a[$b]'
367f 'a[${c:-1}]'
368# (... and presumably most of the other features there)
369# command substitution, yikes!
370f 'a[$(echo 1)]'
371# arithmetic expansion
372f 'a[$(( 3 - 2 ))]'
373
374# All of these are undocumented and probably shouldn't exist,
375# though it's always possible some will turn up in the wild and
376# we'll end up implementing them.
377
378## STDOUT:
379works: a[1]
380works: a[$b]
381works: a[${c:-1}]
382works: a[$(echo 1)]
383works: a[$(( 3 - 2 ))]
384## END
385
386#### Bizarre tilde expansion in array index
387a=(x y)
388PWD=1
389ref='a[~+]'
390echo ${!ref}
391## status: 1
392## BUG bash status: 0
393## BUG bash STDOUT:
394y
395## END
396
397#### Indirect expansion TO fancy expansion features bash disallows
398
399check_indir() {
400 result="${!1}"
401 desugared_result=$(eval 'echo "${'"$1"'}"')
402 [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; }
403}
404x=y
405y=a
406a=(x y)
407declare -A aa
408aa=([k]=r [l]=s)
409# malformed array indexing
410check_indir "a[0"
411check_indir "aa[k"
412# double indirection
413check_indir "!x" a
414check_indir "!a[0]" y
415# apparently everything else in the manual under "Shell Parameter Expansion"
416check_indir "x:-foo" y
417check_indir "x:=foo" y
418check_indir "x:?oops" y
419check_indir "x:+yy" yy
420check_indir "x:0" y
421check_indir "x:0:1" y
422check_indir "!a@" "a aa"
423# (!a[@] is elsewhere)
424check_indir "#x" 1
425check_indir "x#y"
426check_indir "x/y/foo" foo
427check_indir "x@Q" "'y'"
428echo done
429## status: 1
430## stdout-json: ""
431## OK bash status: 0
432## OK bash stdout: done
433
434#### Bad var ref
435a='bad var name'
436echo ref ${!a}
437echo status=$?
438
439## STDOUT:
440status=1
441## END
442## OK osh stdout-json: ""
443## OK osh status: 1
444
445#### Bad var ref 2
446b='/' # really bad
447echo ref ${!b}
448echo status=$?
449## STDOUT:
450status=1
451## END
452## OK osh stdout-json: ""
453## OK osh status: 1
454
455#### ${!OPTIND} (used by bash completion
456set -- a b c
457echo ${!OPTIND}
458f() {
459 local OPTIND=1
460 echo ${!OPTIND}
461 local OPTIND=2
462 echo ${!OPTIND}
463}
464f x y z
465## STDOUT:
466a
467x
468y
469## END
470
471#### var ref doesn't need cycle detection
472x=y
473y=x
474echo cycle=${!x}
475
476typeset -n a=b
477typeset -n b=a
478echo cycle=${a}
479## status: 1
480## STDOUT:
481cycle=x
482## END
483## OK bash status: 0
484## OK bash STDOUT:
485cycle=x
486cycle=
487## END
488
489#### Var Ref Code Injection $(tee PWNED)
490
491typeset -a a
492a=(42)
493
494x='a[$(echo 0 | tee PWNED)]'
495
496echo ${!x}
497
498if test -f PWNED; then
499 echo PWNED
500 cat PWNED
501else
502 echo NOPE
503fi
504
505## status: 1
506## STDOUT:
507## END
508
509## BUG bash status: 0
510## BUG bash STDOUT:
51142
512PWNED
5130
514## END
515
516#### ${!array_ref:-set} and ${!array_ref:=assign}
517
518ref='a[@]'
519a=('' '' '')
520
521echo "==== check ===="
522
523argv.py "${!ref:-set}"
524argv.py "${a[@]:-set}"
525
526echo "==== assign ===="
527
528argv.py "${!ref:=assign}"
529argv.py "${!ref}"
530a=('' '' '') # revert the state in case it is modified
531
532argv.py "${a[@]:=assign}"
533argv.py "${a[@]}"
534
535## STDOUT:
536==== check ====
537['', '', '']
538['', '', '']
539==== assign ====
540['', '', '']
541['', '', '']
542['', '', '']
543['', '', '']
544## END
545
546#### Array indirect expansion with suffix operators
547
548declare -A ref=(['dummy']=v1)
549function test-suffixes {
550 echo "==== $1 ===="
551 ref['dummy']=$1
552 argv.py "${!ref[@]:2}"
553 argv.py "${!ref[@]:1:2}"
554 argv.py "${!ref[@]:-empty}"
555 argv.py "${!ref[@]:+set}"
556 argv.py "${!ref[@]:=assign}"
557}
558
559v1=value
560test-suffixes v1
561echo "v1=$v1"
562
563v2=
564test-suffixes v2
565echo "v2=$v2"
566
567a1=()
568test-suffixes a1
569argv.py "${a1[@]}"
570
571a2=(element)
572test-suffixes 'a2[0]'
573argv.py "${a2[@]}"
574
575a3=(1 2 3)
576test-suffixes 'a3[@]'
577argv.py "${a3[@]}"
578
579## STDOUT:
580==== v1 ====
581['lue']
582['al']
583['value']
584['set']
585['value']
586v1=value
587==== v2 ====
588['']
589['']
590['empty']
591['']
592['assign']
593v2=assign
594==== a1 ====
595['']
596['']
597['empty']
598['']
599['assign']
600['assign']
601==== a2[0] ====
602['ement']
603['le']
604['element']
605['set']
606['element']
607['element']
608==== a3[@] ====
609['3']
610['2', '3']
611['1', '2', '3']
612['set']
613['1', '2', '3']
614['1', '2', '3']
615## END
616
617#### Array indirect expansion with replacements
618
619declare -A ref=(['dummy']=v1)
620function test-rep {
621 echo "==== $1 ===="
622 ref['dummy']=$1
623 argv.py "${!ref[@]#?}"
624 argv.py "${!ref[@]%?}"
625 argv.py "${!ref[@]//[a-f]}"
626 argv.py "${!ref[@]//[a-f]/x}"
627}
628
629v1=value
630test-rep v1
631
632v2=
633test-rep v2
634
635a1=()
636test-rep a1
637
638a2=(element)
639test-rep 'a2[0]'
640
641a3=(1 2 3)
642test-rep 'a3[@]'
643
644## STDOUT:
645==== v1 ====
646['alue']
647['valu']
648['vlu']
649['vxlux']
650==== v2 ====
651['']
652['']
653['']
654['']
655==== a1 ====
656['']
657['']
658['']
659['']
660==== a2[0] ====
661['lement']
662['elemen']
663['lmnt']
664['xlxmxnt']
665==== a3[@] ====
666['', '', '']
667['', '', '']
668['1', '2', '3']
669['1', '2', '3']
670## END
671
672## BUG bash STDOUT:
673==== v1 ====
674['alue']
675['valu']
676['vlu']
677['vxlux']
678==== v2 ====
679['']
680['']
681['']
682['']
683==== a1 ====
684['']
685['']
686['']
687['']
688==== a2[0] ====
689['lement']
690['elemen']
691['lmnt']
692['xlxmxnt']
693==== a3[@] ====
694[]
695[]
696['1', '2', '3']
697['1', '2', '3']
698## END
699
700#### Array indirect expansion with @? conversion
701
702declare -A ref=(['dummy']=v1)
703function test-op0 {
704 echo "==== $1 ===="
705 ref['dummy']=$1
706 argv.py "${!ref[@]@Q}"
707 argv.py "${!ref[@]@P}"
708 argv.py "${!ref[@]@a}"
709}
710
711v1=value
712test-op0 v1
713
714v2=
715test-op0 v2
716
717a1=()
718test-op0 a1
719
720a2=(element)
721test-op0 'a2[0]'
722
723a3=(1 2 3)
724test-op0 'a3[@]'
725
726## STDOUT:
727==== v1 ====
728['value']
729['value']
730['']
731==== v2 ====
732['assign']
733['assign']
734['']
735==== a1 ====
736['assign']
737['assign']
738['a']
739==== a2[0] ====
740['element']
741['element']
742['a']
743==== a3[@] ====
744['1', '2', '3']
745['1', '2', '3']
746['a', 'a', 'a']
747## END
748
749## OK bash STDOUT:
750==== v1 ====
751["'value'"]
752['value']
753['']
754==== v2 ====
755["'assign'"]
756['assign']
757['']
758==== a1 ====
759["'assign'"]
760['assign']
761['a']
762==== a2[0] ====
763["'element'"]
764['element']
765['a']
766==== a3[@] ====
767["'1'", "'2'", "'3'"]
768['1', '2', '3']
769['a', 'a', 'a']
770## END