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