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