1 #!/usr/bin/env bash
2
3 # TODO: Need a SETUP section.
4
5 #### SETUP
6 a=(1 '2 3')
7
8 #### "${a[@]}" and "${a[*]}"
9 a=(1 '2 3')
10 argv.py "${a[@]}" "${a[*]}"
11 ## stdout: ['1', '2 3', '1 2 3']
12
13 #### ${a[@]} and ${a[*]}
14 a=(1 '2 3')
15 argv.py ${a[@]} ${a[*]}
16 ## stdout: ['1', '2', '3', '1', '2', '3']
17
18 #### 4 ways to interpolate empty array
19 argv.py 1 "${a[@]}" 2 ${a[@]} 3 "${a[*]}" 4 ${a[*]} 5
20 ## stdout: ['1', '2', '3', '', '4', '5']
21
22 #### empty array
23 empty=()
24 argv.py "${empty[@]}"
25 ## stdout: []
26
27 #### Empty array with :-
28 empty=()
29 argv.py ${empty[@]:-not one} "${empty[@]:-not one}"
30 ## stdout: ['not', 'one', 'not one']
31
32 #### nounset with empty array (design bug, makes it hard to use arrays)
33 # http://lists.gnu.org/archive/html/help-bash/2017-09/msg00005.html
34 # NOTE: This used to be a bug in bash 4.3, but is fixed in bash 4.4.
35 set -o nounset
36 empty=()
37 argv.py "${empty[@]}"
38 echo status=$?
39 ## STDOUT:
40 []
41 status=0
42 ## END
43 ## BUG mksh stdout-json: ""
44 ## BUG mksh status: 1
45
46 #### local array
47 # mksh support local variables, but not local arrays, oddly.
48 f() {
49 local a=(1 '2 3')
50 argv.py "${a[0]}"
51 }
52 f
53 ## stdout: ['1']
54 ## status: 0
55 ## BUG mksh status: 1
56 ## BUG mksh stdout-json: ""
57
58 #### Command with with word splitting in array
59 array=('1 2' $(echo '3 4'))
60 argv.py "${array[@]}"
61 ## stdout: ['1 2', '3', '4']
62
63 #### space before ( in array initialization
64 # NOTE: mksh accepts this, but bash doesn't
65 a= (1 '2 3')
66 echo $a
67 ## status: 2
68 ## OK mksh status: 0
69 ## OK mksh stdout: 1
70
71 #### array over multiple lines
72 a=(
73 1
74 '2 3'
75 )
76 argv.py "${a[@]}"
77 ## stdout: ['1', '2 3']
78 ## status: 0
79
80 #### array with invalid token
81 a=(
82 1
83 &
84 '2 3'
85 )
86 argv.py "${a[@]}"
87 ## status: 2
88 ## OK mksh status: 1
89
90 #### array with empty string
91 empty=('')
92 argv.py "${empty[@]}"
93 ## stdout: ['']
94
95 #### Retrieve index
96 a=(1 '2 3')
97 argv.py "${a[1]}"
98 ## stdout: ['2 3']
99
100 #### Retrieve out of bounds index
101 a=(1 '2 3')
102 argv.py "${a[3]}"
103 ## stdout: ['']
104
105 #### Negative index
106 a=(1 '2 3')
107 argv.py "${a[-1]}" "${a[-2]}" "${a[-5]}" # last one out of bounds
108 ## stdout: ['2 3', '1', '']
109 ## N-I mksh stdout: ['', '', '']
110
111 #### Retrieve index that is a variable
112 a=(1 '2 3')
113 i=1
114 argv.py "${a[$i]}"
115 ## stdout: ['2 3']
116
117 #### Retrieve index that is a variable without $
118 a=(1 '2 3')
119 i=5
120 argv.py "${a[i-4]}"
121 ## stdout: ['2 3']
122
123 #### Retrieve index that is a command sub
124 a=(1 '2 3')
125 argv.py "${a[$(echo 1)]}"
126 ## stdout: ['2 3']
127
128 #### Retrieve all indices with !
129 a=(1 '2 3')
130 argv.py "${!a[@]}"
131 ## stdout: ['0', '1']
132
133 #### ${!a[1]} is named ref in bash
134 # mksh ignores it
135 foo=bar
136 a=('1 2' foo '2 3')
137 argv.py "${!a[1]}"
138 ## status: 0
139 ## stdout: ['bar']
140 ## N-I mksh stdout: ['a[1]']
141
142 #### ${!a} on array
143 # bash gives empty string?
144 # mksh gives the name of the variable with !. Very weird.
145 a=(1 '2 3')
146 argv.py "${!a}"
147 ## stdout: ['']
148 ## OK mksh stdout: ['a']
149
150 #### All elements unquoted
151 a=(1 '2 3')
152 argv.py ${a[@]}
153 ## stdout: ['1', '2', '3']
154
155 #### All elements quoted
156 a=(1 '2 3')
157 argv.py "${a[@]}"
158 ## stdout: ['1', '2 3']
159
160 #### $*
161 a=(1 '2 3')
162 argv.py ${a[*]}
163 ## stdout: ['1', '2', '3']
164
165 #### "$*"
166 a=(1 '2 3')
167 argv.py "${a[*]}"
168 ## stdout: ['1 2 3']
169
170 #### Interpolate array into array
171 a=(1 '2 3')
172 a=(0 "${a[@]}" '4 5')
173 argv.py "${a[@]}"
174 ## stdout: ['0', '1', '2 3', '4 5']
175
176 #### Exporting array doesn't do anything, not even first element
177 # bash parses, but doesn't execute.
178 # mksh gives syntax error -- parses differently with 'export'
179 # osh no longer parses this statically.
180 export PYTHONPATH=(a b c)
181 export PYTHONPATH=a # NOTE: in bash, this doesn't work afterward!
182 printenv.py PYTHONPATH
183 ## stdout: None
184 ## OK mksh stdout-json: ""
185 ## OK mksh status: 1
186 ## OK osh stdout-json: ""
187 ## OK osh status: 2
188
189 #### Arrays can't be used as env bindings
190 # Hm bash it treats it as a string!
191 A=a B=(b b) printenv.py A B
192 ## status: 2
193 ## stdout-json: ""
194 ## OK bash stdout-json: "a\n(b b)\n"
195 ## OK bash status: 0
196 ## OK mksh status: 1
197
198 #### Set element
199 a=(1 '2 3')
200 a[0]=9
201 argv.py "${a[@]}"
202 ## stdout: ['9', '2 3']
203
204 #### Set element with var ref
205 a=(1 '2 3')
206 i=0
207 a[$i]=9
208 argv.py "${a[@]}"
209 ## stdout: ['9', '2 3']
210
211 #### Set element with array ref
212 # This makes parsing a little more complex. Anything can be inside [],
213 # including other [].
214 a=(1 '2 3')
215 i=(0 1)
216 a[${i[1]}]=9
217 argv.py "${a[@]}"
218 ## stdout: ['1', '9']
219
220 #### Set array item to array
221 a=(1 2)
222 a[0]=(3 4)
223 echo "status=$?"
224 ## stdout: status=1
225 ## status: 0
226 ## N-I mksh stdout-json: ""
227 ## N-I mksh status: 1
228
229 #### Slice of array with [@]
230 # mksh doesn't support this syntax! It's a bash extension.
231 a=(1 2 3)
232 argv.py "${a[@]:1:2}"
233 ## stdout: ['2', '3']
234 ## N-I mksh status: 1
235 ## N-I mksh stdout-json: ""
236
237 #### Negative slice
238 # mksh doesn't support this syntax! It's a bash extension.
239 # NOTE: for some reason -2) has to be in parens? Ah that's because it
240 # conflicts with :-! That's silly. You can also add a space.
241 a=(1 2 3)
242 argv.py "${a[@]:(-2):1}"
243 ## stdout: ['2']
244 ## N-I mksh status: 1
245 ## N-I mksh stdout-json: ""
246
247 #### Slice with arithmetic
248 a=(1 2 3)
249 i=5
250 argv.py "${a[@]:i-4:2}"
251 ## stdout: ['2', '3']
252 ## N-I mksh status: 1
253 ## N-I mksh stdout-json: ""
254
255 #### Number of elements
256 a=(1 '2 3')
257 echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
258 ## stdout: 2 2
259
260 #### Length of an element
261 a=(1 '2 3')
262 echo "${#a[1]}"
263 ## stdout: 3
264
265 #### Iteration
266 a=(1 '2 3')
267 for v in "${a[@]}"; do
268 echo $v
269 done
270 ## stdout-json: "1\n2 3\n"
271
272 #### glob within array yields separate elements
273 touch _tmp/y.Y _tmp/yy.Y
274 a=(_tmp/*.Y)
275 argv.py "${a[@]}"
276 ## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
277
278 #### declare array and then append
279 declare -a array
280 array+=(a)
281 array+=(b c)
282 argv.py "${array[@]}"
283 ## stdout: ['a', 'b', 'c']
284
285 #### Array syntax in wrong place
286 ls foo=(1 2)
287 ## status: 2
288 ## OK mksh status: 1
289
290 #### Single array with :-
291 # bash does EMPTY ELISION here, unless it's double quoted. mksh has
292 # more sane behavior. OSH is better.
293 single=('')
294 argv.py ${single[@]:-none} x "${single[@]:-none}"
295 ## OK osh stdout: ['x', '']
296 ## OK bash stdout: ['none', 'x', '']
297 ## OK mksh stdout: ['none', 'x', 'none']
298
299 #### Stripping a whole array unquoted
300 # Problem: it joins it first.
301 files=('foo.c' 'sp ace.h' 'bar.c')
302 argv.py ${files[@]%.c}
303 ## status: 0
304 ## stdout: ['foo', 'sp', 'ace.h', 'bar']
305 ## N-I mksh status: 1
306 ## N-I mksh stdout-json: ""
307
308 #### Stripping a whole array quoted
309 files=('foo.c' 'sp ace.h' 'bar.c')
310 argv.py "${files[@]%.c}"
311 ## status: 0
312 ## stdout: ['foo', 'sp ace.h', 'bar']
313 ## N-I mksh status: 1
314 ## N-I mksh stdout-json: ""
315
316 #### Multiple subscripts not allowed
317 # NOTE: bash 4.3 had a bug where it ignored the bad subscript, but now it is
318 # fixed.
319 a=('123' '456')
320 argv.py "${a[0]}" "${a[0][0]}"
321 ## stdout-json: ""
322 ## status: 2
323 ## OK bash/mksh status: 1
324
325 #### Length op, index op, then transform op is not allowed
326 a=('123' '456')
327 echo "${#a[0]}" "${#a[0]/1/xxx}"
328 ## stdout-json: ""
329 ## status: 2
330 ## OK bash/mksh status: 1
331
332 #### Array subscript not allowed on string
333 s='abc'
334 echo ${s[@]}
335 ## BUG bash/mksh status: 0
336 ## BUG bash/mksh stdout: abc
337 ## status: 1
338
339 #### Create a "user" array out of the argv array
340 set -- 'a b' 'c'
341 array1=('x y' 'z')
342 array2=("$@")
343 argv.py "${array1[@]}" "${array2[@]}"
344 ## stdout: ['x y', 'z', 'a b', 'c']
345
346 #### Tilde expansion within array
347 HOME=/home/bob
348 a=(~/src ~/git)
349 echo "${a[@]}"
350 ## stdout: /home/bob/src /home/bob/git
351
352 #### Brace Expansion within Array
353 a=(-{a,b} {c,d}-)
354 echo "${a[@]}"
355 ## stdout: -a -b c- d-
356
357 #### array default
358 default=('1 2' '3')
359 argv.py "${undef[@]:-${default[@]}}"
360 ## stdout: ['1 2', '3']
361
362 #### Singleton Array Copy and Assign. Can't index string with int.
363 a=( '12 3' )
364 b=( "${a[@]}" )
365 c="${a[@]}" # This decays it to a string
366 d=$a # This decays it to a string
367 echo ${#a[0]} ${#b[0]}
368 echo ${#a[@]} ${#b[@]}
369 # osh is intentionally stricter about arrays, and these fail.
370 echo ${#c[0]} ${#d[0]}
371 echo ${#c[@]} ${#d[@]}
372 ## status: 1
373 ## STDOUT:
374 4 4
375 1 1
376 ## END
377 ## OK bash/mksh status: 0
378 ## OK bash/mksh STDOUT:
379 4 4
380 1 1
381 4 4
382 1 1
383 ## END
384
385 #### declare -a / local -a is empty array
386 declare -a myarray
387 argv.py "${myarray[@]}"
388 myarray+=('x')
389 argv.py "${myarray[@]}"
390
391 f() {
392 local -a myarray
393 argv.py "${myarray[@]}"
394 myarray+=('x')
395 argv.py "${myarray[@]}"
396 }
397 f
398 ## STDOUT:
399 []
400 ['x']
401 []
402 ['x']
403 ## END
404
405 #### Create sparse array
406 a=()
407 (( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
408 echo len=${#a[@]}
409 argv.py "${a[@]}"
410 echo "unset=${a[33]}"
411 echo len-of-unset=${#a[33]}
412 ## STDOUT:
413 len=1
414 ['1']
415 unset=
416 len-of-unset=0
417 ## END
418
419 #### Create sparse array implicitly
420 (( a[99]=1 ))
421 echo len=${#a[@]}
422 argv.py "${a[@]}"
423 echo "unset=${a[33]}"
424 echo len-of-unset=${#a[33]}
425 ## STDOUT:
426 len=1
427 ['1']
428 unset=
429 len-of-unset=0
430 ## END
431
432 #### Append sparse arrays
433 a=()
434 (( a[99]=1 ))
435 b=()
436 (( b[33]=2 ))
437 (( b[66]=3 ))
438 a+=( "${b[@]}" )
439 argv.py "${a[@]}"
440 argv.py "${a[99]}" "${a[100]}" "${a[101]}"
441 ## STDOUT:
442 ['1', '2', '3']
443 ['1', '2', '3']
444 ## END
445
446 #### Slice of sparse array with [@]
447 # mksh doesn't support this syntax! It's a bash extension.
448 (( a[33]=1 ))
449 (( a[66]=2 ))
450 (( a[99]=2 ))
451 argv.py "${a[@]:15:2}"
452 ## stdout: ['1', '2']
453 ## N-I mksh status: 1
454 ## N-I mksh stdout-json: ""
455
456 #### Using an array itself as the index
457 # TODO: Fix OSH crash.
458 # NOTE: strict-arith prevents this nonsentical behavior.
459 a[a]=42
460 a[a]=99
461 argv "${a[@]}" "${a[0]}" "${a[42]}" "${a[99]}"
462 ## STDOUT:
463 ['42', '99', '42', '99', '']
464 ## END
465