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}
12 a=b
13 b=c
14 echo ref ${!a} ${a}
15 ## stdout: ref c b
16
17 #### ${!ref-default}
18 ref=x
19 echo x=${!ref-default}
20
21 x=''
22 echo x=${!ref-default}
23
24 x=foo
25 echo x=${!ref-default}
26
27 ## STDOUT:
28 x=default
29 x=
30 x=foo
31 ## END
32
33 #### ${!undef:-}
34 # bash gives empty string, but I feel like this could be an error
35 echo undef=${!undef-'default'}
36 echo undef=${!undef}
37
38 set -u
39 echo NOUNSET
40 echo undef=${!undef-'default'}
41 echo undef=${!undef}
42
43 ## status: 1
44 ## STDOUT:
45 undef=default
46 undef=
47 NOUNSET
48 undef=default
49 ## END
50
51 #### comparison to ${!array[@]} keys (similar SYNTAX)
52
53 declare -a a=(x y)
54 argv.py "${!a[@]}"
55 echo a_keys=$?
56
57 argv.py "${!a}" # missing [] is equivalent to ${!a[0]} ?
58 echo a_nobrackets=$?
59
60 echo ---
61 declare -A A=([A]=a [B]=b)
62
63 argv.py ${!A[@]}
64 echo A_keys=$?
65
66 argv.py "${!A}" # missing [] is equivalent to ${!A[0]} ?
67 echo A_nobrackets=$?
68
69 ## STDOUT:
70 ['0', '1']
71 a_keys=0
72 ['']
73 a_nobrackets=0
74 ---
75 ['A', 'B']
76 A_keys=0
77 ['']
78 A_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
87 argv.py "${!a[@]-default}"
88 echo status=$?
89
90 a=(x y z)
91 argv.py "${!a[@]-default}"
92 echo status=$?
93 ## status: 1
94 ## STDOUT:
95 ['default']
96 status=0
97 ## END
98 ## BUG bash status: 0
99 ## BUG bash STDOUT:
100 ['default']
101 status=0
102 status=1
103 ## END
104
105
106 #### var ref to $@ with @
107 set -- one two
108 ref='@'
109 echo ref=${!ref}
110 ## STDOUT:
111 ref=one two
112 ## END
113
114 #### var ref to $1 and $2 with 1 and 2
115 set -- one two
116 ref1='1'
117 echo ref1=${!ref1}
118 ref2='2'
119 echo ref2=${!ref2}
120
121 ## STDOUT:
122 ref1=one
123 ref2=two
124 ## END
125
126 #### var ref: 1, @, *
127 set -- x y
128 ref=1; argv.py "${!ref}"
129 ref=@; argv.py "${!ref}"
130 ref=*; 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
139 ref='LINENO'
140 echo lineno=${!ref}
141 ## STDOUT:
142 lineno=2
143 ## END
144
145 #### var ref to $? with '?'
146 myfunc() {
147 local ref=$1
148 echo ${!ref}
149 }
150 myfunc FUNCNAME
151 myfunc '?'
152 ## STDOUT:
153 myfunc
154 0
155 ## END
156
157
158 #### Var ref, then assignment with ${ := }
159 z=zz
160 zz=
161 echo ${!z:=foo}
162 echo ${!z:=bar}
163 ## STDOUT:
164 foo
165 foo
166 ## END
167
168 #### Var ref, then error with ${ ? }
169 w=ww
170 ww=
171 echo ${!w:?'my message'}
172 echo done
173 ## status: 1
174 ## STDOUT:
175 ## END
176
177 #### Indirect expansion, THEN suffix operators
178
179 check_eq() {
180 [ "$1" = "$2" ] || { echo "$1 vs $2"; }
181 }
182 check_expand() {
183 val=$(eval "echo \"$1\"")
184 [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; }
185 }
186 check_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.
200 x=xx; xx=aaabcc
201 xd=x
202 check_err '${!!xd}'
203 check_err '${!!x*}'
204 a=(asdf x)
205 check_err '${!!a[*]}'
206 check_err '${!#x}'
207 check_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".
210 check_expand '${!a[1]}' xx
211 b=(aoeu a)
212 check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1]
213 #
214 # Allowed: apparently everything else.
215 y=yy; yy=
216 check_expand '${!y:-foo}' foo
217 check_expand '${!x:-foo}' aaabcc
218
219 check_expand '${!x:?oops}' aaabcc
220
221 check_expand '${!y:+foo}' ''
222 check_expand '${!x:+foo}' foo
223
224 check_expand '${!x:2}' abcc
225 check_expand '${!x:2:2}' ab
226
227 check_expand '${!x#*a}' aabcc
228 check_expand '${!x%%c*}' aaab
229 check_expand '${!x/a*b/d}' dcc
230
231 # ^ operator not fully implemented in OSH
232 #check_expand '${!x^a}' Aaabcc
233
234 p=pp; pp='\$ '
235 check_expand '${!p@P}' '$ '
236 echo ok
237 ## stdout: ok
238
239 #### var ref OF array var -- silent a[0] decay
240 declare -a a=(ale bean)
241 echo first=${!a}
242
243 ale=zzz
244 echo first=${!a}
245
246 ## status: 0
247 ## STDOUT:
248 first=
249 first=zzz
250 ## END
251
252 #### array ref
253
254 declare -a array=(ale bean)
255 ref='array[0]'
256 echo ${!ref}
257 ## status: 0
258 ## STDOUT:
259 ale
260 ## END
261
262 #### array ref with strict_array
263 shopt -s strict_array
264
265 declare -a array=(ale bean)
266 ref='array'
267 echo ${!ref}
268 ## status: 1
269 ## stdout-json: ""
270 ## N-I bash status: 0
271 ## N-I bash STDOUT:
272 ale
273 ## END
274
275 #### var ref TO array var
276 shopt -s compat_array
277
278 declare -a array=(ale bean)
279
280 ref='array' # when compat_array is on, this is like array[0]
281 ref_AT='array[@]'
282
283 echo ${!ref}
284 echo ${!ref_AT}
285
286 ## STDOUT:
287 ale
288 ale bean
289 ## END
290
291 #### var ref TO array var, with subscripts
292 f() {
293 argv.py "${!1}"
294 }
295 f 'nonexistent[0]'
296 array=(x y z)
297 f 'array[0]'
298 f 'array[1+1]'
299 f 'array[@]'
300 f '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]
311 shopt -s compat_array
312
313 declare -A assoc=([ale]=bean [corn]=dip)
314 ref=assoc
315 #ref_AT='assoc[@]'
316
317 # UNQUOTED doesn't work with Oil's parser
318 #ref_SUB='assoc[ale]'
319 ref_SUB='assoc["ale"]'
320
321 ref_SUB_QUOTED='assoc["al"e]'
322
323 ref_SUB_BAD='assoc["bad"]'
324
325 echo ref=${!ref} # compat_array: assoc is equivalent to assoc[0]
326 #echo ref_AT=${!ref_AT}
327 echo ref_SUB=${!ref_SUB}
328 echo ref_SUB_QUOTED=${!ref_SUB_QUOTED}
329 echo ref_SUB_BAD=${!ref_SUB_BAD}
330
331 ## STDOUT:
332 ref=
333 ref_SUB=bean
334 ref_SUB_QUOTED=bean
335 ref_SUB_BAD=
336 ## END
337
338 #### var ref TO array with arbitrary subscripts
339 shopt -s eval_unsafe_arith compat_array
340
341 f() {
342 local val=$(echo "${!1}")
343 if test "$val" = y; then
344 echo "works: $1"
345 fi
346 }
347 # Warmup: nice plain array reference
348 a=(x y)
349 f 'a[1]'
350 #
351 # Not allowed:
352 # no brace expansion
353 f 'a[{1,0}]' # operand expected
354 # no process substitution (but see command substitution below!)
355 f 'a[<(echo x)]' # operand expected
356 # TODO word splitting seems interesting
357 aa="1 0"
358 f 'a[$aa]' # 1 0: syntax error in expression (error token is "0")
359 # no filename globbing
360 f 'a[b*]' # operand expected
361 f 'a[1"]' # bad substitution
362 #
363 # Allowed: most everything else in section 3.5 "Shell Expansions".
364 # shell parameter expansion
365 b=1
366 f 'a[$b]'
367 f 'a[${c:-1}]'
368 # (... and presumably most of the other features there)
369 # command substitution, yikes!
370 f 'a[$(echo 1)]'
371 # arithmetic expansion
372 f '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:
379 works: a[1]
380 works: a[$b]
381 works: a[${c:-1}]
382 works: a[$(echo 1)]
383 works: a[$(( 3 - 2 ))]
384 ## END
385
386 #### Bizarre tilde expansion in array index
387 a=(x y)
388 PWD=1
389 ref='a[~+]'
390 echo ${!ref}
391 ## status: 1
392 ## BUG bash status: 0
393 ## BUG bash STDOUT:
394 y
395 ## END
396
397 #### Indirect expansion TO fancy expansion features bash disallows
398
399 check_indir() {
400 result="${!1}"
401 desugared_result=$(eval 'echo "${'"$1"'}"')
402 [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; }
403 }
404 x=y
405 y=a
406 a=(x y)
407 declare -A aa
408 aa=([k]=r [l]=s)
409 # malformed array indexing
410 check_indir "a[0"
411 check_indir "aa[k"
412 # double indirection
413 check_indir "!x" a
414 check_indir "!a[0]" y
415 # apparently everything else in the manual under "Shell Parameter Expansion"
416 check_indir "x:-foo" y
417 check_indir "x:=foo" y
418 check_indir "x:?oops" y
419 check_indir "x:+yy" yy
420 check_indir "x:0" y
421 check_indir "x:0:1" y
422 check_indir "!a@" "a aa"
423 # (!a[@] is elsewhere)
424 check_indir "#x" 1
425 check_indir "x#y"
426 check_indir "x/y/foo" foo
427 check_indir "x@Q" "'y'"
428 echo done
429 ## status: 1
430 ## stdout-json: ""
431 ## OK bash status: 0
432 ## OK bash stdout: done
433
434 #### Bad var ref
435 a='bad var name'
436 echo ref ${!a}
437 echo status=$?
438
439 ## STDOUT:
440 status=1
441 ## END
442 ## OK osh stdout-json: ""
443 ## OK osh status: 1
444
445 #### Bad var ref 2
446 b='/' # really bad
447 echo ref ${!b}
448 echo status=$?
449 ## STDOUT:
450 status=1
451 ## END
452 ## OK osh stdout-json: ""
453 ## OK osh status: 1
454
455 #### ${!OPTIND} (used by bash completion
456 set -- a b c
457 echo ${!OPTIND}
458 f() {
459 local OPTIND=1
460 echo ${!OPTIND}
461 local OPTIND=2
462 echo ${!OPTIND}
463 }
464 f x y z
465 ## STDOUT:
466 a
467 x
468 y
469 ## END
470
471 #### var ref doesn't need cycle detection
472 x=y
473 y=x
474 echo cycle=${!x}
475
476 typeset -n a=b
477 typeset -n b=a
478 echo cycle=${a}
479 ## status: 1
480 ## STDOUT:
481 cycle=x
482 ## END
483 ## OK bash status: 0
484 ## OK bash STDOUT:
485 cycle=x
486 cycle=
487 ## END
488
489 #### Var Ref Code Injection $(tee PWNED)
490
491 typeset -a a
492 a=(42)
493
494 x='a[$(echo 0 | tee PWNED)]'
495
496 echo ${!x}
497
498 if test -f PWNED; then
499 echo PWNED
500 cat PWNED
501 else
502 echo NOPE
503 fi
504
505 ## status: 1
506 ## STDOUT:
507 ## END
508
509 ## BUG bash status: 0
510 ## BUG bash STDOUT:
511 42
512 PWNED
513 0
514 ## END
515
516 #### ${!array_ref:-set} and ${!array_ref:=assign}
517
518 ref='a[@]'
519 a=('' '' '')
520
521 echo "==== check ===="
522
523 argv.py "${!ref:-set}"
524 argv.py "${a[@]:-set}"
525
526 echo "==== assign ===="
527
528 argv.py "${!ref:=assign}"
529 argv.py "${!ref}"
530 a=('' '' '') # revert the state in case it is modified
531
532 argv.py "${a[@]:=assign}"
533 argv.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
548 declare -A ref=(['dummy']=v1)
549 function 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
559 v1=value
560 test-suffixes v1
561 echo "v1=$v1"
562
563 v2=
564 test-suffixes v2
565 echo "v2=$v2"
566
567 a1=()
568 test-suffixes a1
569 argv.py "${a1[@]}"
570
571 a2=(element)
572 test-suffixes 'a2[0]'
573 argv.py "${a2[@]}"
574
575 a3=(1 2 3)
576 test-suffixes 'a3[@]'
577 argv.py "${a3[@]}"
578
579 ## STDOUT:
580 ==== v1 ====
581 ['lue']
582 ['al']
583 ['value']
584 ['set']
585 ['value']
586 v1=value
587 ==== v2 ====
588 ['']
589 ['']
590 ['empty']
591 ['']
592 ['assign']
593 v2=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
619 declare -A ref=(['dummy']=v1)
620 function 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
629 v1=value
630 test-rep v1
631
632 v2=
633 test-rep v2
634
635 a1=()
636 test-rep a1
637
638 a2=(element)
639 test-rep 'a2[0]'
640
641 a3=(1 2 3)
642 test-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
702 declare -A ref=(['dummy']=v1)
703 function 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
711 v1=value
712 test-op0 v1
713
714 v2=
715 test-op0 v2
716
717 a1=()
718 test-op0 a1
719
720 a2=(element)
721 test-op0 'a2[0]'
722
723 a3=(1 2 3)
724 test-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