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