1 ## compare_shells: bash
2 ## oils_failures_allowed: 3
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
72 #### Initializing indexed array with assoc array
73 declare -a a=([xx]=1 [yy]=2 [zz]=3)
74 echo status=$?
75 argv.py "${a[@]}"
76 ## STDOUT:
77 status=2
78 []
79 ## END
80 ## BUG bash STDOUT:
81 status=0
82 ['3']
83 ## END
84
85 #### create empty assoc array, put, then get
86 declare -A A # still undefined
87 argv.py "${A[@]}"
88 argv.py "${!A[@]}"
89 A['foo']=bar
90 echo ${A['foo']}
91 ## STDOUT:
92 []
93 []
94 bar
95 ## END
96
97 #### Empty value (doesn't use EmptyWord?)
98 declare -A A=(["k"]= )
99 argv.py "${A["k"]}"
100 ## STDOUT:
101 ['']
102 ## END
103
104 #### retrieve keys with !
105 declare -A a
106 var='x'
107 a["$var"]=b
108 a['foo']=bar
109 a['a+1']=c
110 for key in "${!a[@]}"; do
111 echo $key
112 done | sort
113 ## STDOUT:
114 a+1
115 foo
116 x
117 ## END
118
119 #### retrieve values with ${A[@]}
120 declare -A A
121 var='x'
122 A["$var"]=b
123 A['foo']=bar
124 A['a+1']=c
125 for val in "${A[@]}"; do
126 echo $val
127 done | sort
128 ## STDOUT:
129 b
130 bar
131 c
132 ## END
133
134 #### coerce to string with ${A[*]}, etc.
135 declare -A A
136 A['X X']=xx
137 A['Y Y']=yy
138 argv.py "${A[*]}"
139 argv.py "${!A[*]}"
140
141 argv.py ${A[@]}
142 argv.py ${!A[@]}
143 ## STDOUT:
144 ['xx yy']
145 ['X X Y Y']
146 ['xx', 'yy']
147 ['X', 'X', 'Y', 'Y']
148 ## END
149
150 #### ${A[@]/b/B}
151 # but ${!A[@]/b/B} doesn't work
152 declare -A A
153 A['aa']=bbb
154 A['bb']=ccc
155 A['cc']=ddd
156 for val in "${A[@]//b/B}"; do
157 echo $val
158 done | sort
159 ## STDOUT:
160 BBB
161 ccc
162 ddd
163 ## END
164
165 #### ${A[@]#prefix}
166 declare -A A
167 A['aa']=one
168 A['bb']=two
169 A['cc']=three
170 for val in "${A[@]#t}"; do
171 echo $val
172 done | sort
173 ## STDOUT:
174 hree
175 one
176 wo
177 ## END
178
179 #### ${assoc} is like ${assoc[0]}
180 declare -A a
181
182 a=([aa]=b [foo]=bar ['a+1']=c)
183 echo a="${a}"
184
185 a=([0]=zzz)
186 echo a="${a}"
187
188 a=(['0']=yyy)
189 echo a="${a}"
190
191 ## STDOUT:
192 a=
193 a=zzz
194 a=yyy
195 ## END
196
197 #### length ${#a[@]}
198 declare -A a
199 a["x"]=1
200 a["y"]=2
201 a["z"]=3
202 echo "${#a[@]}"
203 ## stdout: 3
204
205 #### lookup with ${a[0]} -- "0" is a string
206 declare -A a
207 a["0"]=a
208 a["1"]=b
209 a["2"]=c
210 echo 0 "${a[0]}" 1 "${a[1]}" 2 "${a[2]}"
211 ## STDOUT:
212 0 a 1 b 2 c
213 ## END
214
215 #### lookup with double quoted strings "mykey"
216 declare -A a
217 a["aa"]=b
218 a["foo"]=bar
219 a['a+1']=c
220 echo "${a["aa"]}" "${a["foo"]}" "${a["a+1"]}"
221 ## STDOUT:
222 b bar c
223 ## END
224
225 #### lookup with single quoted string
226 declare -A a
227 a["aa"]=b
228 a["foo"]=bar
229 a['a+1']=c
230 echo "${a['a+1']}"
231 ## stdout: c
232
233 #### lookup with unquoted $key and quoted "$i$i"
234 declare -A A
235 A["aa"]=b
236 A["foo"]=bar
237
238 key=foo
239 echo ${A[$key]}
240 i=a
241 echo ${A["$i$i"]} # note: ${A[$i$i]} doesn't work in OSH
242 ## STDOUT:
243 bar
244 b
245 ## END
246
247 #### lookup by unquoted string doesn't work in OSH because it's a variable
248 declare -A a
249 a["aa"]=b
250 a["foo"]=bar
251 a['a+1']=c
252 echo "${a[a+1]}"
253 ## stdout-json: ""
254 ## status: 1
255 ## BUG bash stdout: c
256 ## BUG bash status: 0
257
258 #### bash bug: "i+1" and i+1 are the same key
259
260 i=1
261 array=(5 6 7)
262 echo array[i]="${array[i]}"
263 echo array[i+1]="${array[i+1]}"
264
265 # arithmetic does NOT work here in bash. These are unquoted strings!
266 declare -A assoc
267 assoc[i]=$i
268 assoc[i+1]=$i+1
269
270 assoc["i"]=string
271 assoc["i+1"]=string+1
272
273 echo assoc[i]="${assoc[i]}"
274 echo assoc[i+1]="${assoc[i+1]}"
275
276 echo assoc[i]="${assoc["i"]}"
277 echo assoc[i+1]="${assoc["i+1"]}"
278
279 ## status: 1
280 ## STDOUT:
281 array[i]=6
282 array[i+1]=7
283 ## END
284 ## BUG bash status: 0
285 ## BUG bash STDOUT:
286 array[i]=6
287 array[i+1]=7
288 assoc[i]=string
289 assoc[i+1]=string+1
290 assoc[i]=string
291 assoc[i+1]=string+1
292 ## END
293
294 #### Array stored in associative array gets converted to string (without strict_array)
295
296 array=('1 2' 3)
297 declare -A d
298 d['key']="${array[@]}"
299 argv.py "${d['key']}"
300 ## stdout: ['1 2 3']
301
302 #### Indexed array as key of associative array coerces to string (without shopt -s strict_array)
303
304 declare -a array=(1 2 3)
305 declare -A assoc
306 assoc[42]=43
307 assoc["${array[@]}"]=foo
308
309 echo "${assoc["${array[@]}"]}"
310 for entry in "${!assoc[@]}"; do
311 echo $entry
312 done | sort
313
314 ## STDOUT:
315 foo
316 1 2 3
317 42
318 ## END
319
320 #### Append to associative array value A['x']+='suffix'
321 declare -A A
322 A['x']='foo'
323 A['x']+='bar'
324 A['x']+='bar'
325 argv.py "${A["x"]}"
326 ## STDOUT:
327 ['foobarbar']
328 ## END
329
330 #### Slice of associative array doesn't make sense in bash
331 declare -A a
332 a[xx]=1
333 a[yy]=2
334 a[zz]=3
335 a[aa]=4
336 a[bb]=5
337 #argv.py ${a["xx"]}
338 argv.py ${a[@]: 0: 3}
339 argv.py ${a[@]: 1: 3}
340 argv.py ${a[@]: 2: 3}
341 argv.py ${a[@]: 3: 3}
342 argv.py ${a[@]: 4: 3}
343 argv.py ${a[@]: 5: 3}
344 ## stdout-json: ""
345 ## status: 1
346 ## BUG bash STDOUT:
347 ['2', '1', '5']
348 ['2', '1', '5']
349 ['1', '5', '4']
350 ['5', '4', '3']
351 ['4', '3']
352 ['3']
353 ## END
354 ## BUG bash status: 0
355
356 #### bash variable can have an associative array part and a string part
357
358 # and $assoc is equivalent to ${assoc[0]}, just like regular arrays
359 declare -A assoc
360 assoc[1]=1
361 assoc[2]=2
362 echo ${assoc[1]} ${assoc[2]} ${assoc}
363 assoc[0]=zero
364 echo ${assoc[1]} ${assoc[2]} ${assoc}
365 assoc=string
366 echo ${assoc[1]} ${assoc[2]} ${assoc}
367 ## STDOUT:
368 1 2
369 1 2 zero
370 1 2 string
371 ## END
372 ## N-I osh status: 1
373 ## N-I osh STDOUT:
374 1 2
375 1 2 zero
376 ## END
377
378 #### Associative array expressions inside (( )) with keys that look like numbers
379 declare -A assoc
380 assoc[0]=42
381 (( var = ${assoc[0]} ))
382 echo $var
383 (( var = assoc[0] ))
384 echo $var
385 ## STDOUT:
386 42
387 42
388 ## END
389
390 #### (( A[5] += 42 ))
391 declare -A A
392 (( A[5] = 10 ))
393 (( A[5] += 6 ))
394 echo ${A[5]}
395 ## STDOUT:
396 16
397 ## END
398
399 #### (( A[5] += 42 )) with empty cell
400 shopt -u strict_arith # default zero cell
401 declare -A A
402 (( A[5] += 6 ))
403 echo ${A[5]}
404 ## STDOUT:
405 6
406 ## END
407
408 #### setting key to itself (from bash-bug mailing list)
409 declare -A foo
410 foo=(["key"]="value1")
411 echo ${foo["key"]}
412 foo=(["key"]="${foo["key"]} value2")
413 echo ${foo["key"]}
414 ## STDOUT:
415 value1
416 value1 value2
417 ## END
418 ## BUG bash STDOUT:
419 value1
420 value2
421 ## END
422
423 #### readonly associative array can't be modified
424 declare -Ar A
425 A['x']=1
426 echo status=$?
427 ## OK osh status: 1
428 ## OK osh stdout-json: ""
429 ## STDOUT:
430 status=1
431 ## END
432
433 #### associative array and brace expansion
434 declare -A A=([k1]=v [k2]=-{a,b}-)
435 echo ${A["k1"]}
436 echo ${A["k2"]}
437 ## STDOUT:
438 v
439 -{a,b}-
440 ## END
441
442 #### bash mangles array #1
443 a=([k1]=v1 [k2]=v2)
444 echo ${a["k1"]}
445 echo ${a["k2"]}
446 ## STDOUT:
447 v1
448 v2
449 ## END
450 ## BUG bash STDOUT:
451 v2
452 v2
453 ## END
454
455 #### bash mangles array and brace #2
456 a=([k2]=-{a,b}-)
457 echo ${a["k2"]}
458 ## STDOUT:
459 -{a,b}-
460 ## END
461 ## BUG bash STDOUT:
462 [k2]=-a-
463 ## END
464
465 #### declare -A A=() allowed
466 set -o nounset
467 shopt -s strict_arith || true
468
469 declare -A ASSOC=()
470 echo len=${#ASSOC[@]}
471
472 # Check that it really can be used like an associative array
473 ASSOC['k']='32'
474 echo len=${#ASSOC[@]}
475
476 # bash allows a variable to be an associative array AND unset, while OSH
477 # doesn't
478 set +o nounset
479 declare -A u
480 echo unset len=${#u[@]}
481 ## STDOUT:
482 len=0
483 len=1
484 unset len=0
485 ## END
486
487 #### unset -v and assoc array
488 shopt -s eval_unsafe_arith || true
489
490 show-len() {
491 echo len=${#assoc[@]}
492 }
493
494 declare -A assoc=(['K']=val)
495 show-len
496
497 unset -v 'assoc["K"]'
498 show-len
499
500 declare -A assoc=(['K']=val)
501 show-len
502 key=K
503 unset -v 'assoc[$key]'
504 show-len
505
506 declare -A assoc=(['K']=val)
507 show-len
508 unset -v 'assoc[$(echo K)]'
509 show-len
510
511 # ${prefix} doesn't work here, even though it does in arithmetic
512 #declare -A assoc=(['K']=val)
513 #show-len
514 #prefix=as
515 #unset -v '${prefix}soc[$key]'
516 #show-len
517
518 ## STDOUT:
519 len=1
520 len=0
521 len=1
522 len=0
523 len=1
524 len=0
525 ## END
526
527 #### nameref and assoc array
528 show-values() {
529 echo values: ${A[@]}
530 }
531
532 declare -A A=(['K']=val)
533 show-values
534
535 declare -n ref='A["K"]'
536 echo before $ref
537 ref='val2'
538 echo after $ref
539 show-values
540
541 echo ---
542
543 key=K
544 declare -n ref='A[$key]'
545 echo before $ref
546 ref='val3'
547 echo after $ref
548 show-values
549
550 ## STDOUT:
551 values: val
552 before val
553 after val2
554 values: val2
555 ---
556 before val2
557 after val3
558 values: val3
559 ## END
560
561 #### ${!ref} and assoc array
562
563 show-values() {
564 echo values: ${A[@]}
565 }
566
567 declare -A A=(['K']=val)
568 show-values
569
570 declare ref='A["K"]'
571 echo ref ${!ref}
572
573 key=K
574 declare ref='A[$key]'
575 echo ref ${!ref}
576
577 ## STDOUT:
578 values: val
579 ref val
580 ref val
581 ## END
582
583 #### printf -v and assoc array
584
585 show-values() {
586 echo values: ${assoc[@]}
587 }
588
589 declare -A assoc=(['K']=val)
590 show-values
591
592 printf -v 'assoc["K"]' '/%s/' val2
593 show-values
594
595 key=K
596 printf -v 'assoc[$key]' '/%s/' val3
597 show-values
598
599 # Somehow bash doesn't allow this
600 #prefix=as
601 #printf -v '${prefix}soc[$key]' '/%s/' val4
602 #show-values
603
604 ## STDOUT:
605 values: val
606 values: /val2/
607 values: /val3/
608 ## END
609
610 #### bash bug: (( A["$key"] = 1 )) doesn't work
611 key='\'
612 declare -A A
613 #A["$key"]=1
614
615 # Works in both
616 #A["$key"]=42
617
618 # Works in bash only
619 #(( A[\$key] = 42 ))
620
621 (( A["$key"] = 42 ))
622
623 argv.py "${!A[@]}"
624 argv.py "${A[@]}"
625 ## STDOUT:
626 ['\\']
627 ['42']
628 ## END
629 ## BUG bash STDOUT:
630 []
631 []
632 ## END
633
634
635 #### Implicit increment of keys
636 declare -a arr=( [30]=a b [40]=x y)
637 argv.py "${!arr[@]}"
638 argv.py "${arr[@]}"
639
640 # osh says "expected associative array pair"
641
642 ## STDOUT:
643 ['30', '31', '40', '41']
644 ['a', 'b', 'x', 'y']
645 ## END