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 / set -u with empty array (bug in bash 4.3, fixed in 4.4)
32
33 # http://lists.gnu.org/archive/html/help-bash/2017-09/msg00005.html
34
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 #### Negative index and sparse array
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 a=(0 1)
150 unset 'a[-1]' # remove last element
151 a+=(2 3)
152 echo ${a[0]} $((a[0]))
153 echo ${a[1]} $((a[1]))
154 echo ${a[2]} $((a[2]))
155 echo ${a[3]} $((a[3]))
156 ## STDOUT:
157 0 0
158 2 2
159 3 3
160 0
161 ## END
162 ## BUG mksh STDOUT:
163 0 0
164 1 1
165 2 2
166 3 3
167 ## END
168
169 #### Length after unset
170 a=(0 1 2 3)
171 unset a[-1]
172 echo len=${#a[@]}
173 unset a[-1]
174 echo len=${#a[@]}
175 ## STDOUT:
176 len=3
177 len=2
178 ## END
179 ## BUG mksh STDOUT:
180 len=4
181 len=4
182 ## END
183
184 #### Retrieve index that is a variable
185 a=(1 '2 3')
186 i=1
187 argv.py "${a[$i]}"
188 ## stdout: ['2 3']
189
190 #### Retrieve index that is a variable without $
191 a=(1 '2 3')
192 i=5
193 argv.py "${a[i-4]}"
194 ## stdout: ['2 3']
195
196 #### Retrieve index that is a command sub
197 a=(1 '2 3')
198 argv.py "${a[$(echo 1)]}"
199 ## stdout: ['2 3']
200
201 #### Retrieve array indices with ${!a}
202 a=(1 '2 3')
203 argv.py "${!a[@]}"
204 ## stdout: ['0', '1']
205
206 #### Retrieve sparse array indices with ${!a}
207 a=()
208 (( a[99]=1 ))
209 argv.py "${!a[@]}"
210 ## STDOUT:
211 ['99']
212 ## END
213
214 #### ${!a[1]} is named ref in bash
215 # mksh ignores it
216 foo=bar
217 a=('1 2' foo '2 3')
218 argv.py "${!a[1]}"
219 ## status: 0
220 ## stdout: ['bar']
221 ## N-I mksh stdout: ['a[1]']
222
223 #### ${!a} on array
224
225 # bash gives empty string because it's like a[0]
226 # mksh gives the name of the variable with !. Very weird.
227
228 a=(1 '2 3')
229 argv.py "${!a}"
230
231 ## stdout: ['']
232 ## status: 0
233 ## BUG mksh stdout: ['a']
234 ## BUG mksh status: 0
235
236 #### All elements unquoted
237 a=(1 '2 3')
238 argv.py ${a[@]}
239 ## stdout: ['1', '2', '3']
240
241 #### All elements quoted
242 a=(1 '2 3')
243 argv.py "${a[@]}"
244 ## stdout: ['1', '2 3']
245
246 #### $*
247 a=(1 '2 3')
248 argv.py ${a[*]}
249 ## stdout: ['1', '2', '3']
250
251 #### "$*"
252 a=(1 '2 3')
253 argv.py "${a[*]}"
254 ## stdout: ['1 2 3']
255
256 #### Interpolate array into array
257 a=(1 '2 3')
258 a=(0 "${a[@]}" '4 5')
259 argv.py "${a[@]}"
260 ## stdout: ['0', '1', '2 3', '4 5']
261
262 #### Exporting array doesn't do anything, not even first element
263 # bash parses, but doesn't execute.
264 # mksh gives syntax error -- parses differently with 'export'
265 # osh no longer parses this statically.
266 export PYTHONPATH=(a b c)
267 export PYTHONPATH=a # NOTE: in bash, this doesn't work afterward!
268 printenv.py PYTHONPATH
269 ## stdout-json: ""
270 ## status: 1
271 ## OK bash stdout: None
272 ## OK bash status: 0
273
274 #### Arrays can't be used as env bindings
275 # Hm bash it treats it as a string!
276 A=a B=(b b) printenv.py A B
277 ## status: 2
278 ## stdout-json: ""
279 ## OK bash stdout-json: "a\n(b b)\n"
280 ## OK bash status: 0
281 ## OK mksh status: 1
282
283 #### Set element
284 a=(1 '2 3')
285 a[0]=9
286 argv.py "${a[@]}"
287 ## stdout: ['9', '2 3']
288
289 #### Set element with var ref
290 a=(1 '2 3')
291 i=0
292 a[$i]=9
293 argv.py "${a[@]}"
294 ## stdout: ['9', '2 3']
295
296 #### Set element with array ref
297 # This makes parsing a little more complex. Anything can be inside [],
298 # including other [].
299 a=(1 '2 3')
300 i=(0 1)
301 a[${i[1]}]=9
302 argv.py "${a[@]}"
303 ## stdout: ['1', '9']
304
305 #### Set array item to array
306 a=(1 2)
307 a[0]=(3 4)
308 echo "status=$?"
309 ## stdout-json: ""
310 ## status: 2
311 ## N-I mksh status: 1
312 ## BUG bash stdout: status=1
313 ## BUG bash status: 0
314
315 #### Slice of array with [@]
316 # mksh doesn't support this syntax! It's a bash extension.
317 a=(1 2 3)
318 argv.py "${a[@]:1:2}"
319 ## stdout: ['2', '3']
320 ## N-I mksh status: 1
321 ## N-I mksh stdout-json: ""
322
323 #### Negative slice begin
324 # mksh doesn't support this syntax! It's a bash extension.
325 # NOTE: for some reason -2) has to be in parens? Ah that's because it
326 # conflicts with :-! That's silly. You can also add a space.
327 a=(1 2 3 4 5)
328 argv.py "${a[@]:(-4)}"
329 ## stdout: ['2', '3', '4', '5']
330 ## N-I mksh status: 1
331 ## N-I mksh stdout-json: ""
332
333 #### Negative slice length
334 a=(1 2 3 4 5)
335 argv.py "${a[@]: 1: -3}"
336 ## status: 1
337 ## stdout-json: ""
338
339 #### Slice with arithmetic
340 a=(1 2 3)
341 i=5
342 argv.py "${a[@]:i-4:2}"
343 ## stdout: ['2', '3']
344 ## N-I mksh status: 1
345 ## N-I mksh stdout-json: ""
346
347 #### Number of elements
348 a=(1 '2 3')
349 echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
350 ## stdout: 2 2
351
352 #### Length of an element
353 a=(1 '2 3')
354 echo "${#a[1]}"
355 ## stdout: 3
356
357 #### Iteration
358 a=(1 '2 3')
359 for v in "${a[@]}"; do
360 echo $v
361 done
362 ## stdout-json: "1\n2 3\n"
363
364 #### glob within array yields separate elements
365 touch _tmp/y.Y _tmp/yy.Y
366 a=(_tmp/*.Y)
367 argv.py "${a[@]}"
368 ## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
369
370 #### declare array and then append
371 declare -a array
372 array+=(a)
373 array+=(b c)
374 argv.py "${array[@]}"
375 ## stdout: ['a', 'b', 'c']
376
377 #### Array syntax in wrong place
378 ls foo=(1 2)
379 ## status: 1
380 ## OK bash status: 2
381
382 #### Single array with :-
383 # bash does EMPTY ELISION here, unless it's double quoted. mksh has
384 # more sane behavior. OSH is better.
385 single=('')
386 argv.py ${single[@]:-none} x "${single[@]:-none}"
387 ## OK osh stdout: ['x', '']
388 ## OK bash stdout: ['none', 'x', '']
389 ## OK mksh stdout: ['none', 'x', 'none']
390
391 #### Stripping a whole array unquoted
392 # Problem: it joins it first.
393 files=('foo.c' 'sp ace.h' 'bar.c')
394 argv.py ${files[@]%.c}
395 ## status: 0
396 ## stdout: ['foo', 'sp', 'ace.h', 'bar']
397 ## N-I mksh status: 1
398 ## N-I mksh stdout-json: ""
399
400 #### Stripping a whole array quoted
401 files=('foo.c' 'sp ace.h' 'bar.c')
402 argv.py "${files[@]%.c}"
403 ## status: 0
404 ## stdout: ['foo', 'sp ace.h', 'bar']
405 ## N-I mksh status: 1
406 ## N-I mksh stdout-json: ""
407
408 #### Multiple subscripts not allowed
409 # NOTE: bash 4.3 had a bug where it ignored the bad subscript, but now it is
410 # fixed.
411 a=('123' '456')
412 argv.py "${a[0]}" "${a[0][0]}"
413 ## stdout-json: ""
414 ## status: 2
415 ## OK bash/mksh status: 1
416
417 #### Length op, index op, then transform op is not allowed
418 a=('123' '456')
419 echo "${#a[0]}" "${#a[0]/1/xxx}"
420 ## stdout-json: ""
421 ## status: 2
422 ## OK bash/mksh status: 1
423
424 #### ${mystr[@]} and ${mystr[*]} are no-ops
425 s='abc'
426 echo ${s[@]}
427 echo ${s[*]}
428 ## STDOUT:
429 abc
430 abc
431 ## END
432
433 #### ${mystr[@]} and ${mystr[*]} disallowed with strict_array
434
435 $SH -c 'shopt -s strict_array; s="abc"; echo ${s[@]}'
436 echo status=$?
437
438 $SH -c 'shopt -s strict_array; s="abc"; echo ${s[*]}'
439 echo status=$?
440
441 ## status: 0
442 ## STDOUT:
443 status=1
444 status=1
445 ## END
446 ## N-I bash/mksh STDOUT:
447 abc
448 status=0
449 abc
450 status=0
451 ## END
452
453 #### Create a "user" array out of the argv array
454 set -- 'a b' 'c'
455 array1=('x y' 'z')
456 array2=("$@")
457 argv.py "${array1[@]}" "${array2[@]}"
458 ## stdout: ['x y', 'z', 'a b', 'c']
459
460 #### Tilde expansion within array
461 HOME=/home/bob
462 a=(~/src ~/git)
463 echo "${a[@]}"
464 ## stdout: /home/bob/src /home/bob/git
465
466 #### Brace Expansion within Array
467 a=(-{a,b} {c,d}-)
468 echo "${a[@]}"
469 ## stdout: -a -b c- d-
470
471 #### array default
472 default=('1 2' '3')
473 argv.py "${undef[@]:-${default[@]}}"
474 ## stdout: ['1 2', '3']
475
476 #### Singleton Array Copy and Assign. OSH can't index strings with ints
477 a=( '12 3' )
478 b=( "${a[@]}" )
479 c="${a[@]}" # This decays it to a string
480 d=${a[*]} # This decays it to a string
481 echo ${#a[0]} ${#b[0]}
482 echo ${#a[@]} ${#b[@]}
483
484 # osh is intentionally stricter, and these fail.
485 echo ${#c[0]} ${#d[0]}
486 echo ${#c[@]} ${#d[@]}
487
488 ## status: 1
489 ## STDOUT:
490 4 4
491 1 1
492 ## END
493 ## OK bash/mksh status: 0
494 ## OK bash/mksh STDOUT:
495 4 4
496 1 1
497 4 4
498 1 1
499 ## END
500
501 #### declare -a / local -a is empty array
502 declare -a myarray
503 argv.py "${myarray[@]}"
504 myarray+=('x')
505 argv.py "${myarray[@]}"
506
507 f() {
508 local -a myarray
509 argv.py "${myarray[@]}"
510 myarray+=('x')
511 argv.py "${myarray[@]}"
512 }
513 f
514 ## STDOUT:
515 []
516 ['x']
517 []
518 ['x']
519 ## END
520
521 #### Create sparse array
522 a=()
523 (( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
524 echo len=${#a[@]}
525 argv.py "${a[@]}"
526 echo "unset=${a[33]}"
527 echo len-of-unset=${#a[33]}
528 ## STDOUT:
529 len=1
530 ['1']
531 unset=
532 len-of-unset=0
533 ## END
534
535 #### Create sparse array implicitly
536 (( a[99]=1 ))
537 echo len=${#a[@]}
538 argv.py "${a[@]}"
539 echo "unset=${a[33]}"
540 echo len-of-unset=${#a[33]}
541 ## STDOUT:
542 len=1
543 ['1']
544 unset=
545 len-of-unset=0
546 ## END
547
548 #### Append sparse arrays
549 a=()
550 (( a[99]=1 ))
551 b=()
552 (( b[33]=2 ))
553 (( b[66]=3 ))
554 a+=( "${b[@]}" )
555 argv.py "${a[@]}"
556 argv.py "${a[99]}" "${a[100]}" "${a[101]}"
557 ## STDOUT:
558 ['1', '2', '3']
559 ['1', '2', '3']
560 ## END
561
562 #### Slice of sparse array with [@]
563 # mksh doesn't support this syntax! It's a bash extension.
564 (( a[33]=1 ))
565 (( a[66]=2 ))
566 (( a[99]=2 ))
567 argv.py "${a[@]:15:2}"
568 ## stdout: ['1', '2']
569 ## N-I mksh status: 1
570 ## N-I mksh stdout-json: ""
571
572 #### Using an array itself as the index on LHS
573 shopt -u strict_arith
574 a[a]=42
575 a[a]=99
576 argv.py "${a[@]}" "${a[0]}" "${a[42]}" "${a[99]}"
577
578 ## status: 0
579 ## STDOUT:
580 ['42', '99', '42', '99', '']
581 ## END
582
583 #### Using an array itself as the index on RHS
584 shopt -u strict_arith
585 a=(1 2 3)
586 (( x = a[a] ))
587 echo $x
588 ## status: 0
589 ## STDOUT:
590 2
591 ## END
592
593 #### a[$x$y] on LHS and RHS
594 x=1
595 y=2
596 a[$x$y]=foo
597
598 # not allowed by OSH parsing
599 #echo ${a[$x$y]}
600
601 echo ${a[12]}
602 echo ${#a[@]}
603
604 ## STDOUT:
605 foo
606 1
607 ## END
608
609
610 #### Dynamic parsing of LHS a[$code]=value
611
612 declare -a array
613 array[x=1]='one'
614
615 code='y=2'
616 #code='1+2' # doesn't work either
617 array[$code]='two'
618
619 argv.py "${array[@]}"
620 echo x=$x
621 echo y=$y
622
623 ## STDOUT:
624 ['one', 'two']
625 x=1
626 y=2
627 ## END
628 ## N-I dash stdout-json: ""
629 ## N-I dash status: 2
630
631 #### Dynamic parsing of RHS ${a[$code]}
632 declare -a array
633 array=(zero one two three)
634
635 echo ${array[1+2]}
636
637 code='1+2'
638 echo ${array[$code]}
639
640 ## STDOUT:
641 three
642 three
643 ## END
644
645 # it still dynamically parses
646
647 ## OK zsh STDOUT:
648 two
649 two
650 ## END