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