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
267 export PYTHONPATH
268
269 PYTHONPATH=mystr # NOTE: in bash, this doesn't work afterward!
270 printenv.py PYTHONPATH
271
272 PYTHONPATH=(myarray)
273 printenv.py PYTHONPATH
274
275 PYTHONPATH=(a b c)
276 printenv.py PYTHONPATH
277
278 ## status: 0
279 ## STDOUT:
280 mystr
281 None
282 None
283 ## END
284
285 #### strict_array prevents exporting array
286
287 shopt -s strict_array
288
289 export PYTHONPATH
290 PYTHONPATH=(a b c)
291 printenv.py PYTHONPATH
292
293 ## status: 1
294 ## STDOUT:
295 ## END
296
297 ## N-I bash/mksh status: 0
298 ## N-I bash/mksh STDOUT:
299 None
300 ## END
301
302 #### Arrays can't be used as env bindings
303 # Hm bash it treats it as a string!
304 A=a B=(b b) printenv.py A B
305 ## status: 2
306 ## stdout-json: ""
307 ## OK bash stdout-json: "a\n(b b)\n"
308 ## OK bash status: 0
309 ## OK mksh status: 1
310
311 #### Set element
312 a=(1 '2 3')
313 a[0]=9
314 argv.py "${a[@]}"
315 ## stdout: ['9', '2 3']
316
317 #### Set element with var ref
318 a=(1 '2 3')
319 i=0
320 a[$i]=9
321 argv.py "${a[@]}"
322 ## stdout: ['9', '2 3']
323
324 #### Set element with array ref
325 # This makes parsing a little more complex. Anything can be inside [],
326 # including other [].
327 a=(1 '2 3')
328 i=(0 1)
329 a[${i[1]}]=9
330 argv.py "${a[@]}"
331 ## stdout: ['1', '9']
332
333 #### Set array item to array
334 a=(1 2)
335 a[0]=(3 4)
336 echo "status=$?"
337 ## stdout-json: ""
338 ## status: 2
339 ## N-I mksh status: 1
340 ## BUG bash stdout: status=1
341 ## BUG bash status: 0
342
343 #### Slice of array with [@]
344 # mksh doesn't support this syntax! It's a bash extension.
345 a=(1 2 3)
346 argv.py "${a[@]:1:2}"
347 ## stdout: ['2', '3']
348 ## N-I mksh status: 1
349 ## N-I mksh stdout-json: ""
350
351 #### Negative slice begin
352 # mksh doesn't support this syntax! It's a bash extension.
353 # NOTE: for some reason -2) has to be in parens? Ah that's because it
354 # conflicts with :-! That's silly. You can also add a space.
355 a=(1 2 3 4 5)
356 argv.py "${a[@]:(-4)}"
357 ## stdout: ['2', '3', '4', '5']
358 ## N-I mksh status: 1
359 ## N-I mksh stdout-json: ""
360
361 #### Negative slice length
362 a=(1 2 3 4 5)
363 argv.py "${a[@]: 1: -3}"
364 ## status: 1
365 ## stdout-json: ""
366
367 #### Slice with arithmetic
368 a=(1 2 3)
369 i=5
370 argv.py "${a[@]:i-4:2}"
371 ## stdout: ['2', '3']
372 ## N-I mksh status: 1
373 ## N-I mksh stdout-json: ""
374
375 #### Number of elements
376 a=(1 '2 3')
377 echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
378 ## stdout: 2 2
379
380 #### Length of an element
381 a=(1 '2 3')
382 echo "${#a[1]}"
383 ## stdout: 3
384
385 #### Iteration
386 a=(1 '2 3')
387 for v in "${a[@]}"; do
388 echo $v
389 done
390 ## stdout-json: "1\n2 3\n"
391
392 #### glob within array yields separate elements
393 touch _tmp/y.Y _tmp/yy.Y
394 a=(_tmp/*.Y)
395 argv.py "${a[@]}"
396 ## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
397
398 #### declare array and then append
399 declare -a array
400 array+=(a)
401 array+=(b c)
402 argv.py "${array[@]}"
403 ## stdout: ['a', 'b', 'c']
404
405 #### Array syntax in wrong place
406 ls foo=(1 2)
407 ## status: 1
408 ## OK bash status: 2
409
410 #### Single array with :-
411 # bash does EMPTY ELISION here, unless it's double quoted. mksh has
412 # more sane behavior. OSH is better.
413 single=('')
414 argv.py ${single[@]:-none} x "${single[@]:-none}"
415 ## OK osh stdout: ['x', '']
416 ## OK bash stdout: ['none', 'x', '']
417 ## OK mksh stdout: ['none', 'x', 'none']
418
419 #### Stripping a whole array unquoted
420 # Problem: it joins it first.
421 files=('foo.c' 'sp ace.h' 'bar.c')
422 argv.py ${files[@]%.c}
423 ## status: 0
424 ## stdout: ['foo', 'sp', 'ace.h', 'bar']
425 ## N-I mksh status: 1
426 ## N-I mksh stdout-json: ""
427
428 #### Stripping a whole array quoted
429 files=('foo.c' 'sp ace.h' 'bar.c')
430 argv.py "${files[@]%.c}"
431 ## status: 0
432 ## stdout: ['foo', 'sp ace.h', 'bar']
433 ## N-I mksh status: 1
434 ## N-I mksh stdout-json: ""
435
436 #### Multiple subscripts not allowed
437 # NOTE: bash 4.3 had a bug where it ignored the bad subscript, but now it is
438 # fixed.
439 a=('123' '456')
440 argv.py "${a[0]}" "${a[0][0]}"
441 ## stdout-json: ""
442 ## status: 2
443 ## OK bash/mksh status: 1
444
445 #### Length op, index op, then transform op is not allowed
446 a=('123' '456')
447 echo "${#a[0]}" "${#a[0]/1/xxx}"
448 ## stdout-json: ""
449 ## status: 2
450 ## OK bash/mksh status: 1
451
452 #### ${mystr[@]} and ${mystr[*]} are no-ops
453 s='abc'
454 echo ${s[@]}
455 echo ${s[*]}
456 ## STDOUT:
457 abc
458 abc
459 ## END
460
461 #### ${mystr[@]} and ${mystr[*]} disallowed with strict_array
462
463 $SH -c 'shopt -s strict_array; s="abc"; echo ${s[@]}'
464 echo status=$?
465
466 $SH -c 'shopt -s strict_array; s="abc"; echo ${s[*]}'
467 echo status=$?
468
469 ## status: 0
470 ## STDOUT:
471 status=1
472 status=1
473 ## END
474 ## N-I bash/mksh STDOUT:
475 abc
476 status=0
477 abc
478 status=0
479 ## END
480
481 #### Create a "user" array out of the argv array
482 set -- 'a b' 'c'
483 array1=('x y' 'z')
484 array2=("$@")
485 argv.py "${array1[@]}" "${array2[@]}"
486 ## stdout: ['x y', 'z', 'a b', 'c']
487
488 #### Tilde expansion within array
489 HOME=/home/bob
490 a=(~/src ~/git)
491 echo "${a[@]}"
492 ## stdout: /home/bob/src /home/bob/git
493
494 #### Brace Expansion within Array
495 a=(-{a,b} {c,d}-)
496 echo "${a[@]}"
497 ## stdout: -a -b c- d-
498
499 #### array default
500 default=('1 2' '3')
501 argv.py "${undef[@]:-${default[@]}}"
502 ## stdout: ['1 2', '3']
503
504 #### Singleton Array Copy and Assign. OSH can't index strings with ints
505 a=( '12 3' )
506 b=( "${a[@]}" )
507 c="${a[@]}" # This decays it to a string
508 d=${a[*]} # This decays it to a string
509 echo ${#a[0]} ${#b[0]}
510 echo ${#a[@]} ${#b[@]}
511
512 # osh is intentionally stricter, and these fail.
513 echo ${#c[0]} ${#d[0]}
514 echo ${#c[@]} ${#d[@]}
515
516 ## status: 1
517 ## STDOUT:
518 4 4
519 1 1
520 ## END
521 ## OK bash/mksh status: 0
522 ## OK bash/mksh STDOUT:
523 4 4
524 1 1
525 4 4
526 1 1
527 ## END
528
529 #### declare -a / local -a is empty array
530 declare -a myarray
531 argv.py "${myarray[@]}"
532 myarray+=('x')
533 argv.py "${myarray[@]}"
534
535 f() {
536 local -a myarray
537 argv.py "${myarray[@]}"
538 myarray+=('x')
539 argv.py "${myarray[@]}"
540 }
541 f
542 ## STDOUT:
543 []
544 ['x']
545 []
546 ['x']
547 ## END
548
549 #### Create sparse array
550 a=()
551 (( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
552 echo len=${#a[@]}
553 argv.py "${a[@]}"
554 echo "unset=${a[33]}"
555 echo len-of-unset=${#a[33]}
556 ## STDOUT:
557 len=1
558 ['1']
559 unset=
560 len-of-unset=0
561 ## END
562
563 #### Create sparse array implicitly
564 (( a[99]=1 ))
565 echo len=${#a[@]}
566 argv.py "${a[@]}"
567 echo "unset=${a[33]}"
568 echo len-of-unset=${#a[33]}
569 ## STDOUT:
570 len=1
571 ['1']
572 unset=
573 len-of-unset=0
574 ## END
575
576 #### Append sparse arrays
577 a=()
578 (( a[99]=1 ))
579 b=()
580 (( b[33]=2 ))
581 (( b[66]=3 ))
582 a+=( "${b[@]}" )
583 argv.py "${a[@]}"
584 argv.py "${a[99]}" "${a[100]}" "${a[101]}"
585 ## STDOUT:
586 ['1', '2', '3']
587 ['1', '2', '3']
588 ## END
589
590 #### Slice of sparse array with [@]
591 # mksh doesn't support this syntax! It's a bash extension.
592 (( a[33]=1 ))
593 (( a[66]=2 ))
594 (( a[99]=2 ))
595 argv.py "${a[@]:15:2}"
596 ## stdout: ['1', '2']
597 ## N-I mksh status: 1
598 ## N-I mksh stdout-json: ""
599
600 #### Using an array itself as the index on LHS
601 shopt -u strict_arith
602 a[a]=42
603 a[a]=99
604 argv.py "${a[@]}" "${a[0]}" "${a[42]}" "${a[99]}"
605
606 ## status: 0
607 ## STDOUT:
608 ['42', '99', '42', '99', '']
609 ## END
610
611 #### Using an array itself as the index on RHS
612 shopt -u strict_arith
613 a=(1 2 3)
614 (( x = a[a] ))
615 echo $x
616 ## status: 0
617 ## STDOUT:
618 2
619 ## END
620
621 #### a[$x$y] on LHS and RHS
622 x=1
623 y=2
624 a[$x$y]=foo
625
626 # not allowed by OSH parsing
627 #echo ${a[$x$y]}
628
629 echo ${a[12]}
630 echo ${#a[@]}
631
632 ## STDOUT:
633 foo
634 1
635 ## END
636
637
638 #### Dynamic parsing of LHS a[$code]=value
639
640 declare -a array
641 array[x=1]='one'
642
643 code='y=2'
644 #code='1+2' # doesn't work either
645 array[$code]='two'
646
647 argv.py "${array[@]}"
648 echo x=$x
649 echo y=$y
650
651 ## STDOUT:
652 ['one', 'two']
653 x=1
654 y=2
655 ## END
656 ## N-I dash stdout-json: ""
657 ## N-I dash status: 2
658
659 #### Dynamic parsing of RHS ${a[$code]}
660 declare -a array
661 array=(zero one two three)
662
663 echo ${array[1+2]}
664
665 code='1+2'
666 echo ${array[$code]}
667
668 ## STDOUT:
669 three
670 three
671 ## END
672
673 # it still dynamically parses
674
675 ## OK zsh STDOUT:
676 two
677 two
678 ## END