OILS / spec / array-assoc.test.sh View on Github | oils.pub

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