1 #!/usr/bin/env bash
2
3 # NOTE:
4 # -declare -A is required.
5 #
6 # Simply doing:
7 # a=([aa]=b [foo]=bar ['a+1']=c)
8 # gets utterly bizarre behavior.
9 #
10 # Associtative Arrays are COMPLETELY bash-specific. mksh doesn't even come
11 # close. So I will probably not implement them, or implement something
12 # slightly different, because the semantics are just wierd.
13
14 # http://www.gnu.org/software/bash/manual/html_node/Arrays.html
15 # TODO: Need a SETUP section.
16
17 #### Literal syntax ([x]=y)
18 declare -A a
19 a=([aa]=b [foo]=bar ['a+1']=c)
20 echo ${a["aa"]}
21 echo ${a["foo"]}
22 echo ${a["a+1"]}
23 ## STDOUT:
24 b
25 bar
26 c
27 ## END
28
29 #### set associative array to indexed array literal (very surprising bash behavior)
30 declare -A assoc=([k1]=foo [k2]='spam eggs')
31 for v in "${assoc[@]}"; do echo $v; done | sort
32 for v in "${!assoc[@]}"; do echo $v; done | sort
33
34 # disallow this in OSH? Changing type?
35
36 assoc=(foo 'spam eggs')
37 argv.py "${assoc[@]}"
38 argv.py "${!assoc[@]}"
39
40 ## STDOUT:
41 foo
42 spam eggs
43 k1
44 k2
45 ['foo', 'spam eggs']
46 ['0', '1']
47 ## END
48 ## BUG bash STDOUT:
49 foo
50 spam eggs
51 k1
52 k2
53 []
54 []
55 ## END
56
57 #### Can't initialize assoc array with indexed array
58 declare -A A=(1 2 3)
59 echo status=$?
60 ## STDOUT:
61 status=1
62 ## END
63
64 # bash prints warnings to stderr but gives no indication of the problem
65 ## BUG bash STDOUT:
66 status=0
67 ## END
68
69
70 #### Initializing indexed array with assoc array
71 declare -a a=([xx]=1 [yy]=2 [zz]=3)
72 echo status=$?
73 argv.py "${a[@]}"
74 ## STDOUT:
75 status=1
76 []
77 ## END
78 ## BUG bash STDOUT:
79 status=0
80 ['3']
81 ## END
82
83 #### create empty assoc array, put, then get
84 declare -A A # still undefined
85 argv.py "${A[@]}"
86 argv.py "${!A[@]}"
87 A['foo']=bar
88 echo ${A['foo']}
89 ## STDOUT:
90 []
91 []
92 bar
93 ## END
94
95 #### retrieve keys with !
96 declare -A a
97 var='x'
98 a["$var"]=b
99 a['foo']=bar
100 a['a+1']=c
101 for key in "${!a[@]}"; do
102 echo $key
103 done | sort
104 ## STDOUT:
105 a+1
106 foo
107 x
108 ## END
109
110 #### retrieve values with ${A[@]}
111 declare -A A
112 var='x'
113 A["$var"]=b
114 A['foo']=bar
115 A['a+1']=c
116 for val in "${A[@]}"; do
117 echo $val
118 done | sort
119 ## STDOUT:
120 b
121 bar
122 c
123 ## END
124
125 #### coerce to string with ${A[*]}, etc.
126 declare -A A
127 A['X X']=xx
128 A['Y Y']=yy
129 argv.py "${A[*]}"
130 argv.py "${!A[*]}"
131
132 argv.py ${A[@]}
133 argv.py ${!A[@]}
134 ## STDOUT:
135 ['xx yy']
136 ['X X Y Y']
137 ['xx', 'yy']
138 ['X', 'X', 'Y', 'Y']
139 ## END
140
141 #### ${A[@]/b/B}
142 # but ${!A[@]/b/B} doesn't work
143 declare -A A
144 A['aa']=bbb
145 A['bb']=ccc
146 A['cc']=ddd
147 for val in "${A[@]//b/B}"; do
148 echo $val
149 done | sort
150 ## STDOUT:
151 BBB
152 ccc
153 ddd
154 ## END
155
156 #### ${A[@]#prefix}
157 declare -A A
158 A['aa']=one
159 A['bb']=two
160 A['cc']=three
161 for val in "${A[@]#t}"; do
162 echo $val
163 done | sort
164 ## STDOUT:
165 hree
166 one
167 wo
168 ## END
169
170 #### ${assoc} disallowed in OSH, like ${assoc[0]} in bash
171 declare -A a
172 a=([aa]=b [foo]=bar ['a+1']=c)
173 echo "${a}"
174 ## stdout-json: "\n"
175 ## OK osh stdout-json: ""
176 ## OK osh status: 1
177
178 #### length ${#a[@]}
179 declare -A a
180 a["x"]=1
181 a["y"]=2
182 a["z"]=3
183 echo "${#a[@]}"
184 ## stdout: 3
185
186 #### lookup with ${a[0]} -- "0" is a string
187 declare -A a
188 a["0"]=a
189 a["1"]=b
190 a["2"]=c
191 echo 0 "${a[0]}" 1 "${a[1]}" 2 "${a[2]}"
192 ## STDOUT:
193 0 a 1 b 2 c
194 ## END
195
196 #### lookup with double quoted strings "mykey"
197 declare -A a
198 a["aa"]=b
199 a["foo"]=bar
200 a['a+1']=c
201 echo "${a["aa"]}" "${a["foo"]}" "${a["a+1"]}"
202 ## STDOUT:
203 b bar c
204 ## END
205
206 #### lookup with single quoted string
207 declare -A a
208 a["aa"]=b
209 a["foo"]=bar
210 a['a+1']=c
211 echo "${a['a+1']}"
212 ## stdout: c
213
214 #### lookup with unquoted $key and quoted "$i$i"
215 declare -A A
216 A["aa"]=b
217 A["foo"]=bar
218
219 key=foo
220 echo ${A[$key]}
221 i=a
222 echo ${A["$i$i"]} # note: ${A[$i$i]} doesn't work in OSH
223 ## STDOUT:
224 bar
225 b
226 ## END
227
228 #### lookup by unquoted string doesn't work in OSH because it's a variable
229 declare -A a
230 a["aa"]=b
231 a["foo"]=bar
232 a['a+1']=c
233 echo "${a[a+1]}"
234 ## stdout-json: ""
235 ## status: 1
236 ## BUG bash stdout: c
237 ## BUG bash status: 0
238
239 #### bash bug: "i+1" and i+1 are the same key
240
241 i=1
242 array=(5 6 7)
243 echo array[i]="${array[i]}"
244 echo array[i+1]="${array[i+1]}"
245
246 # arithmetic does NOT work here in bash. These are unquoted strings!
247 declare -A assoc
248 assoc[i]=$i
249 assoc[i+1]=$i+1
250
251 assoc["i"]=string
252 assoc["i+1"]=string+1
253
254 echo assoc[i]="${assoc[i]}"
255 echo assoc[i+1]="${assoc[i+1]}"
256
257 echo assoc[i]="${assoc["i"]}"
258 echo assoc[i+1]="${assoc["i+1"]}"
259
260 ## status: 1
261 ## STDOUT:
262 array[i]=6
263 array[i+1]=7
264 ## END
265 ## BUG bash status: 0
266 ## BUG bash STDOUT:
267 array[i]=6
268 array[i+1]=7
269 assoc[i]=string
270 assoc[i+1]=string+1
271 assoc[i]=string
272 assoc[i+1]=string+1
273 ## END
274
275 #### Array stored in associative array gets converted to string (without strict-array)
276
277 array=('1 2' 3)
278 declare -A d
279 d['key']="${array[@]}"
280 argv.py "${d['key']}"
281 ## stdout: ['1 2 3']
282
283 #### Indexed array as key of associative array coerces to string (without shopt -s strict-array)
284
285 declare -a array=(1 2 3)
286 declare -A assoc
287 assoc[42]=43
288 assoc["${array[@]}"]=foo
289
290 echo "${assoc["${array[@]}"]}"
291 for entry in "${!assoc[@]}"; do
292 echo $entry
293 done | sort
294
295 ## STDOUT:
296 foo
297 1 2 3
298 42
299 ## END
300
301 #### Append to associative array value A['x']+='suffix'
302 declare -A A
303 A['x']='foo'
304 A['x']+='bar'
305 A['x']+='bar'
306 argv.py "${A["x"]}"
307 ## STDOUT:
308 ['foobarbar']
309 ## END
310
311 #### Slice of associative array doesn't make sense in bash
312 declare -A a
313 a[xx]=1
314 a[yy]=2
315 a[zz]=3
316 a[aa]=4
317 a[bb]=5
318 #argv.py ${a["xx"]}
319 argv.py ${a[@]: 0: 3}
320 argv.py ${a[@]: 1: 3}
321 argv.py ${a[@]: 2: 3}
322 argv.py ${a[@]: 3: 3}
323 argv.py ${a[@]: 4: 3}
324 argv.py ${a[@]: 5: 3}
325 ## stdout-json: ""
326 ## status: 1
327 ## BUG bash STDOUT:
328 ['2', '1', '5']
329 ['2', '1', '5']
330 ['1', '5', '4']
331 ['5', '4', '3']
332 ['4', '3']
333 ['3']
334 ## END
335 ## BUG bash status: 0
336
337 #### bash variable can have an associative array part and a string part
338
339 # and $assoc is equivalent to ${assoc[0]}, just like regular arrays
340 declare -A assoc
341 assoc[1]=1
342 assoc[2]=2
343 echo ${assoc[1]} ${assoc[2]} ${assoc}
344 assoc[0]=zero
345 echo ${assoc[1]} ${assoc[2]} ${assoc}
346 assoc=string
347 echo ${assoc[1]} ${assoc[2]} ${assoc}
348 ## STDOUT:
349 1 2
350 1 2 zero
351 1 2 string
352 ## END
353 ## N-I osh stdout-json: ""
354 ## N-I osh status: 1
355
356 #### Associative array expressions inside (( )) with keys that look like numbers
357 declare -A assoc
358 assoc[0]=42
359 (( var = ${assoc[0]} ))
360 echo $var
361 (( var = assoc[0] ))
362 echo $var
363 ## STDOUT:
364 42
365 42
366 ## END
367 ## N-I osh status: 1
368 ## N-I osh STDOUT:
369 42
370 ## END
371
372 #### (( A[5] += 42 ))
373 declare -A A
374 (( A[5] = 10 ))
375 (( A[5] += 6 ))
376 echo ${A[5]}
377 ## STDOUT:
378 16
379 ## END
380
381 #### (( A[5] += 42 )) with empty cell
382 shopt -u strict-arith # default zero cell
383 declare -A A
384 (( A[5] += 6 ))
385 echo ${A[5]}
386 ## STDOUT:
387 6
388 ## END
389
390 #### setting key to itself (from bash-bug mailing list)
391 declare -A foo
392 foo=(["key"]="value1")
393 echo ${foo["key"]}
394 foo=(["key"]="${foo["key"]} value2")
395 echo ${foo["key"]}
396 ## STDOUT:
397 value1
398 value1 value2
399 ## END
400 ## BUG bash STDOUT:
401 value1
402 value2
403 ## END
404
405 #### readonly associative array can't be modified
406 declare -Ar A
407 A['x']=1
408 echo status=$?
409 ## OK osh status: 1
410 ## OK osh stdout-json: ""
411 ## STDOUT:
412 status=1
413 ## END