1 ## compare_shells: bash-4.4
2 ## oils_failures_allowed: 2
3
4
5 # NOTE:
6 # -declare -A is required.
7 #
8 # Simply doing:
9 # a=([aa]=b [foo]=bar ['a+1']=c)
10 # gets utterly bizarre behavior.
11 #
12 # Associtative Arrays are COMPLETELY bash-specific. mksh doesn't even come
13 # close. So I will probably not implement them, or implement something
14 # slightly different, because the semantics are just weird.
15
16 # http://www.gnu.org/software/bash/manual/html_node/Arrays.html
17 # TODO: Need a SETUP section.
18
19 #### Literal syntax ([x]=y)
20 declare -A a
21 a=([aa]=b [foo]=bar ['a+1']=c)
22 echo ${a["aa"]}
23 echo ${a["foo"]}
24 echo ${a["a+1"]}
25 ## STDOUT:
26 b
27 bar
28 c
29 ## END
30
31 #### set associative array to indexed array literal (very surprising bash behavior)
32 declare -A assoc=([k1]=foo [k2]='spam eggs')
33 for v in "${assoc[@]}"; do echo $v; done | sort
34 for v in "${!assoc[@]}"; do echo $v; done | sort
35
36 # disallow this in OSH? Changing type?
37
38 assoc=(foo 'spam eggs')
39 argv.py "${assoc[@]}"
40 argv.py "${!assoc[@]}"
41
42 ## STDOUT:
43 foo
44 spam eggs
45 k1
46 k2
47 ['foo', 'spam eggs']
48 ['0', '1']
49 ## END
50 ## BUG bash STDOUT:
51 foo
52 spam eggs
53 k1
54 k2
55 []
56 []
57 ## END
58
59 #### Can't initialize assoc array with indexed array
60 declare -A A=(1 2 3)
61 echo status=$?
62 ## STDOUT:
63 status=2
64 ## END
65
66 # bash prints warnings to stderr but gives no indication of the problem
67 ## BUG bash STDOUT:
68 status=0
69 ## END
70
71 #### create empty assoc array, put, then get
72 declare -A A # still undefined
73 argv.py "${A[@]}"
74 argv.py "${!A[@]}"
75 A['foo']=bar
76 echo ${A['foo']}
77 ## STDOUT:
78 []
79 []
80 bar
81 ## END
82
83 #### Empty value (doesn't use EmptyWord?)
84 declare -A A=(["k"]= )
85 argv.py "${A["k"]}"
86 ## STDOUT:
87 ['']
88 ## END
89
90 #### retrieve keys with !
91 declare -A a
92 var='x'
93 a["$var"]=b
94 a['foo']=bar
95 a['a+1']=c
96 for key in "${!a[@]}"; do
97 echo $key
98 done | sort
99 ## STDOUT:
100 a+1
101 foo
102 x
103 ## END
104
105 #### retrieve values with ${A[@]}
106 declare -A A
107 var='x'
108 A["$var"]=b
109 A['foo']=bar
110 A['a+1']=c
111 for val in "${A[@]}"; do
112 echo $val
113 done | sort
114 ## STDOUT:
115 b
116 bar
117 c
118 ## END
119
120 #### coerce to string with ${A[*]}, etc.
121 declare -A A
122 A['X X']=xx
123 A['Y Y']=yy
124 argv.py "${A[*]}"
125 argv.py "${!A[*]}"
126
127 argv.py ${A[@]}
128 argv.py ${!A[@]}
129 ## STDOUT:
130 ['xx yy']
131 ['X X Y Y']
132 ['xx', 'yy']
133 ['X', 'X', 'Y', 'Y']
134 ## END
135
136 #### ${A[@]/b/B}
137 # but ${!A[@]/b/B} doesn't work
138 declare -A A
139 A['aa']=bbb
140 A['bb']=ccc
141 A['cc']=ddd
142 for val in "${A[@]//b/B}"; do
143 echo $val
144 done | sort
145 ## STDOUT:
146 BBB
147 ccc
148 ddd
149 ## END
150
151 #### ${A[@]#prefix}
152 declare -A A
153 A['aa']=one
154 A['bb']=two
155 A['cc']=three
156 for val in "${A[@]#t}"; do
157 echo $val
158 done | sort
159 ## STDOUT:
160 hree
161 one
162 wo
163 ## END
164
165 #### ${assoc} is like ${assoc[0]}
166 declare -A a
167
168 a=([aa]=b [foo]=bar ['a+1']=c)
169 echo a="${a}"
170
171 a=([0]=zzz)
172 echo a="${a}"
173
174 a=(['0']=yyy)
175 echo a="${a}"
176
177 ## STDOUT:
178 a=
179 a=zzz
180 a=yyy
181 ## END
182
183 #### length ${#a[@]}
184 declare -A a
185 a["x"]=1
186 a["y"]=2
187 a["z"]=3
188 echo "${#a[@]}"
189 ## stdout: 3
190
191 #### lookup with ${a[0]} -- "0" is a string
192 declare -A a
193 a["0"]=a
194 a["1"]=b
195 a["2"]=c
196 echo 0 "${a[0]}" 1 "${a[1]}" 2 "${a[2]}"
197 ## STDOUT:
198 0 a 1 b 2 c
199 ## END
200
201 #### lookup with double quoted strings "mykey"
202 declare -A a
203 a["aa"]=b
204 a["foo"]=bar
205 a['a+1']=c
206 echo "${a["aa"]}" "${a["foo"]}" "${a["a+1"]}"
207 ## STDOUT:
208 b bar c
209 ## END
210
211 #### lookup with single quoted string
212 declare -A a
213 a["aa"]=b
214 a["foo"]=bar
215 a['a+1']=c
216 echo "${a['a+1']}"
217 ## stdout: c
218
219 #### lookup with unquoted $key and quoted "$i$i"
220 declare -A A
221 A["aa"]=b
222 A["foo"]=bar
223
224 key=foo
225 echo ${A[$key]}
226 i=a
227 echo ${A["$i$i"]} # note: ${A[$i$i]} doesn't work in OSH
228 ## STDOUT:
229 bar
230 b
231 ## END
232
233 #### lookup by unquoted string doesn't work in OSH because it's a variable
234 declare -A a
235 a["aa"]=b
236 a["foo"]=bar
237 a['a+1']=c
238 echo "${a[a+1]}"
239 ## stdout-json: ""
240 ## status: 1
241 ## BUG bash stdout: c
242 ## BUG bash status: 0
243
244 #### bash bug: "i+1" and i+1 are the same key
245
246 i=1
247 array=(5 6 7)
248 echo array[i]="${array[i]}"
249 echo array[i+1]="${array[i+1]}"
250
251 # arithmetic does NOT work here in bash. These are unquoted strings!
252 declare -A assoc
253 assoc[i]=$i
254 assoc[i+1]=$i+1
255
256 assoc["i"]=string
257 assoc["i+1"]=string+1
258
259 echo assoc[i]="${assoc[i]}"
260 echo assoc[i+1]="${assoc[i+1]}"
261
262 echo assoc[i]="${assoc["i"]}"
263 echo assoc[i+1]="${assoc["i+1"]}"
264
265 ## status: 1
266 ## STDOUT:
267 array[i]=6
268 array[i+1]=7
269 ## END
270 ## BUG bash status: 0
271 ## BUG bash STDOUT:
272 array[i]=6
273 array[i+1]=7
274 assoc[i]=string
275 assoc[i+1]=string+1
276 assoc[i]=string
277 assoc[i+1]=string+1
278 ## END
279
280 #### Array stored in associative array gets converted to string (without strict_array)
281
282 array=('1 2' 3)
283 declare -A d
284 d['key']="${array[@]}"
285 argv.py "${d['key']}"
286 ## stdout: ['1 2 3']
287
288 #### Indexed array as key of associative array coerces to string (without shopt -s strict_array)
289
290 declare -a array=(1 2 3)
291 declare -A assoc
292 assoc[42]=43
293 assoc["${array[@]}"]=foo
294
295 echo "${assoc["${array[@]}"]}"
296 for entry in "${!assoc[@]}"; do
297 echo $entry
298 done | sort
299
300 ## STDOUT:
301 foo
302 1 2 3
303 42
304 ## END
305
306 #### Append to associative array value A['x']+='suffix'
307 declare -A A
308 A['x']='foo'
309 A['x']+='bar'
310 A['x']+='bar'
311 argv.py "${A["x"]}"
312 ## STDOUT:
313 ['foobarbar']
314 ## END
315
316 #### Slice of associative array doesn't make sense in bash
317 declare -A a
318 a[xx]=1
319 a[yy]=2
320 a[zz]=3
321 a[aa]=4
322 a[bb]=5
323 #argv.py ${a["xx"]}
324 argv.py ${a[@]: 0: 3}
325 argv.py ${a[@]: 1: 3}
326 argv.py ${a[@]: 2: 3}
327 argv.py ${a[@]: 3: 3}
328 argv.py ${a[@]: 4: 3}
329 argv.py ${a[@]: 5: 3}
330 ## stdout-json: ""
331 ## status: 1
332 ## BUG bash STDOUT:
333 ['2', '1', '5']
334 ['2', '1', '5']
335 ['1', '5', '4']
336 ['5', '4', '3']
337 ['4', '3']
338 ['3']
339 ## END
340 ## BUG bash status: 0
341
342 #### bash variable can have an associative array part and a string part
343
344 # and $assoc is equivalent to ${assoc[0]}, just like regular arrays
345 declare -A assoc
346 assoc[1]=1
347 assoc[2]=2
348 echo ${assoc[1]} ${assoc[2]} ${assoc}
349 assoc[0]=zero
350 echo ${assoc[1]} ${assoc[2]} ${assoc}
351 assoc=string
352 echo ${assoc[1]} ${assoc[2]} ${assoc}
353 ## STDOUT:
354 1 2
355 1 2 zero
356 1 2 string
357 ## END
358 ## N-I osh status: 1
359 ## N-I osh STDOUT:
360 1 2
361 1 2 zero
362 ## END
363
364 #### Associative array expressions inside (( )) with keys that look like numbers
365 declare -A assoc
366 assoc[0]=42
367 (( var = ${assoc[0]} ))
368 echo $var
369 (( var = assoc[0] ))
370 echo $var
371 ## STDOUT:
372 42
373 42
374 ## END
375
376 #### (( A[5] += 42 ))
377 declare -A A
378 (( A[5] = 10 ))
379 (( A[5] += 6 ))
380 echo ${A[5]}
381 ## STDOUT:
382 16
383 ## END
384
385 #### (( A[5] += 42 )) with empty cell
386 shopt -u strict_arith # default zero cell
387 declare -A A
388 (( A[5] += 6 ))
389 echo ${A[5]}
390 ## STDOUT:
391 6
392 ## END
393
394 #### setting key to itself (from bash-bug mailing list)
395 declare -A foo
396 foo=(["key"]="value1")
397 echo ${foo["key"]}
398 foo=(["key"]="${foo["key"]} value2")
399 echo ${foo["key"]}
400 ## STDOUT:
401 value1
402 value1 value2
403 ## END
404 ## BUG bash STDOUT:
405 value1
406 value2
407 ## END
408
409 #### readonly associative array can't be modified
410 declare -Ar A
411 A['x']=1
412 echo status=$?
413 ## OK osh status: 1
414 ## OK osh stdout-json: ""
415 ## STDOUT:
416 status=1
417 ## END
418
419 #### associative array and brace expansion
420 declare -A A=([k1]=v [k2]=-{a,b}-)
421 echo ${A["k1"]}
422 echo ${A["k2"]}
423 ## STDOUT:
424 v
425 -{a,b}-
426 ## END
427
428 #### bash mangles indexed array #1 (associative array is OK)
429 declare -A a
430 a=([k1]=v1 [k2]=v2)
431 echo ${a["k1"]}
432 echo ${a["k2"]}
433 ## STDOUT:
434 v1
435 v2
436 ## END
437
438 #### bash mangles indexed array #2 (associative array is OK)
439 declare -A a
440 a=([k2]=-{a,b}-)
441 echo ${a["k2"]}
442 ## STDOUT:
443 -{a,b}-
444 ## END
445
446 #### declare -A A=() allowed
447 set -o nounset
448 shopt -s strict_arith || true
449
450 declare -A ASSOC=()
451 echo len=${#ASSOC[@]}
452
453 # Check that it really can be used like an associative array
454 ASSOC['k']='32'
455 echo len=${#ASSOC[@]}
456
457 # bash allows a variable to be an associative array AND unset, while OSH
458 # doesn't
459 set +o nounset
460 declare -A u
461 echo unset len=${#u[@]}
462 ## STDOUT:
463 len=0
464 len=1
465 unset len=0
466 ## END
467
468 #### unset -v and assoc array
469 shopt -s eval_unsafe_arith || true
470
471 show-len() {
472 echo len=${#assoc[@]}
473 }
474
475 declare -A assoc=(['K']=val)
476 show-len
477
478 unset -v 'assoc["K"]'
479 show-len
480
481 declare -A assoc=(['K']=val)
482 show-len
483 key=K
484 unset -v 'assoc[$key]'
485 show-len
486
487 declare -A assoc=(['K']=val)
488 show-len
489 unset -v 'assoc[$(echo K)]'
490 show-len
491
492 # ${prefix} doesn't work here, even though it does in arithmetic
493 #declare -A assoc=(['K']=val)
494 #show-len
495 #prefix=as
496 #unset -v '${prefix}soc[$key]'
497 #show-len
498
499 ## STDOUT:
500 len=1
501 len=0
502 len=1
503 len=0
504 len=1
505 len=0
506 ## END
507
508 #### nameref and assoc array
509 show-values() {
510 echo values: ${A[@]}
511 }
512
513 declare -A A=(['K']=val)
514 show-values
515
516 declare -n ref='A["K"]'
517 echo before $ref
518 ref='val2'
519 echo after $ref
520 show-values
521
522 echo ---
523
524 key=K
525 declare -n ref='A[$key]'
526 echo before $ref
527 ref='val3'
528 echo after $ref
529 show-values
530
531 ## STDOUT:
532 values: val
533 before val
534 after val2
535 values: val2
536 ---
537 before val2
538 after val3
539 values: val3
540 ## END
541
542 #### ${!ref} and assoc array
543
544 show-values() {
545 echo values: ${A[@]}
546 }
547
548 declare -A A=(['K']=val)
549 show-values
550
551 declare ref='A["K"]'
552 echo ref ${!ref}
553
554 key=K
555 declare ref='A[$key]'
556 echo ref ${!ref}
557
558 ## STDOUT:
559 values: val
560 ref val
561 ref val
562 ## END
563
564 #### printf -v and assoc array
565
566 show-values() {
567 echo values: ${assoc[@]}
568 }
569
570 declare -A assoc=(['K']=val)
571 show-values
572
573 printf -v 'assoc["K"]' '/%s/' val2
574 show-values
575
576 key=K
577 printf -v 'assoc[$key]' '/%s/' val3
578 show-values
579
580 # Somehow bash doesn't allow this
581 #prefix=as
582 #printf -v '${prefix}soc[$key]' '/%s/' val4
583 #show-values
584
585 ## STDOUT:
586 values: val
587 values: /val2/
588 values: /val3/
589 ## END
590
591 #### bash bug: (( A["$key"] = 1 )) doesn't work
592 key='\'
593 declare -A A
594 #A["$key"]=1
595
596 # Works in both
597 #A["$key"]=42
598
599 # Works in bash only
600 #(( A[\$key] = 42 ))
601
602 (( A["$key"] = 42 ))
603
604 argv.py "${!A[@]}"
605 argv.py "${A[@]}"
606 ## STDOUT:
607 ['\\']
608 ['42']
609 ## END
610 ## BUG bash STDOUT:
611 []
612 []
613 ## END
614
615
616 #### Implicit increment of keys
617 declare -a arr=( [30]=a b [40]=x y)
618 argv.py "${!arr[@]}"
619 argv.py "${arr[@]}"
620
621 ## STDOUT:
622 ['30', '31', '40', '41']
623 ['a', 'b', 'x', 'y']
624 ## END
625
626 #### test -v assoc[key]
627
628 typeset -A assoc
629 assoc=([empty]='' [k]=v)
630
631 echo 'no quotes'
632
633 test -v assoc[empty]
634 echo empty=$?
635
636 test -v assoc[k]
637 echo k=$?
638
639 test -v assoc[nonexistent]
640 echo nonexistent=$?
641
642 echo
643
644 # Now with quotes
645 echo 'quotes'
646
647 test -v assoc["empty"]
648 echo empty=$?
649
650 test -v assoc['k']
651 echo k=$?
652
653 test -v assoc['nonexistent']
654 echo nonexistent=$?
655
656 ## STDOUT:
657 no quotes
658 empty=0
659 k=0
660 nonexistent=1
661
662 quotes
663 empty=0
664 k=0
665 nonexistent=1
666 ## END
667
668 #### test -v with dynamic parsing
669
670 typeset -A assoc
671 assoc=([empty]='' [k]=v)
672
673 key=empty
674 test -v 'assoc[$key]'
675 echo empty=$?
676
677 key=k
678 test -v 'assoc[$key]'
679 echo k=$?
680
681 key=nonexistent
682 test -v 'assoc[$key]'
683 echo nonexistent=$?
684
685 ## STDOUT:
686 empty=0
687 k=0
688 nonexistent=1
689 ## END
690
691 #### [[ -v assoc[key] ]]
692
693 typeset -A assoc
694 assoc=([empty]='' [k]=v)
695
696 echo 'no quotes'
697
698 [[ -v assoc[empty] ]]
699 echo empty=$?
700
701 [[ -v assoc[k] ]]
702 echo k=$?
703
704 [[ -v assoc[nonexistent] ]]
705 echo nonexistent=$?
706
707 echo
708
709 # Now with quotes
710 echo 'quotes'
711
712 [[ -v assoc["empty"] ]]
713 echo empty=$?
714
715 [[ -v assoc['k'] ]]
716 echo k=$?
717
718 [[ -v assoc['nonexistent'] ]]
719 echo nonexistent=$?
720
721 echo
722
723 echo 'vars'
724
725 key=empty
726 [[ -v assoc[$key] ]]
727 echo empty=$?
728
729 key=k
730 [[ -v assoc[$key] ]]
731 echo k=$?
732
733 key=nonexistent
734 [[ -v assoc[$key] ]]
735 echo nonexistent=$?
736
737 ## STDOUT:
738 no quotes
739 empty=0
740 k=0
741 nonexistent=1
742
743 quotes
744 empty=0
745 k=0
746 nonexistent=1
747
748 vars
749 empty=0
750 k=0
751 nonexistent=1
752 ## END
753
754 ## N-I mksh status: 1
755 ## N-I mksh STDOUT:
756 ## END
757
758 #### [[ -v assoc[key] ]] syntax errors
759
760 typeset -A assoc
761 assoc=([empty]='' [k]=v)
762
763 [[ -v assoc[empty] ]]
764 echo empty=$?
765
766 [[ -v assoc[k] ]]
767 echo k=$?
768
769 [[ -v assoc[k]z ]]
770 echo typo=$?
771
772 ## STDOUT:
773 empty=0
774 k=0
775 typo=1
776 ## END
777
778
779 #### BashAssoc a+=()
780
781 declare -A a=([apple]=red [orange]=orange)
782 a+=([lemon]=yellow [banana]=yellow)
783 echo "apple is ${a['apple']}"
784 echo "orange is ${a['orange']}"
785 echo "lemon is ${a['lemon']}"
786 echo "banana is ${a['banana']}"
787
788 ## STDOUT:
789 apple is red
790 orange is orange
791 lemon is yellow
792 banana is yellow
793 ## END
794
795
796 #### BashAssoc ${a[@]@Q}
797
798 declare -A a=()
799 a['symbol1']=\'\'
800 a['symbol2']='"'
801 a['symbol3']='()<>&|'
802 a['symbol4']='[]*?'
803 echo "[${a[@]@Q}]"
804 echo "[${a[*]@Q}]"
805
806 ## STDOUT:
807 [$'\'\'' '"' '()<>&|' '[]*?']
808 [$'\'\'' '"' '()<>&|' '[]*?']
809 ## END
810
811 ## OK bash STDOUT:
812 ['[]*?' ''\'''\''' '"' '()<>&|']
813 ['[]*?' ''\'''\''' '"' '()<>&|']
814 ## END