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