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