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