OILS / spec / array.test.sh View on Github | oilshell.org

678 lines, 314 significant
1
2# TODO: Need a SETUP section.
3
4#### SETUP
5a=(1 '2 3')
6
7#### "${a[@]}" and "${a[*]}"
8a=(1 '2 3')
9argv.py "${a[@]}" "${a[*]}"
10## stdout: ['1', '2 3', '1 2 3']
11
12#### ${a[@]} and ${a[*]}
13a=(1 '2 3')
14argv.py ${a[@]} ${a[*]}
15## stdout: ['1', '2', '3', '1', '2', '3']
16
17#### 4 ways to interpolate empty array
18argv.py 1 "${a[@]}" 2 ${a[@]} 3 "${a[*]}" 4 ${a[*]} 5
19## stdout: ['1', '2', '3', '', '4', '5']
20
21#### empty array
22empty=()
23argv.py "${empty[@]}"
24## stdout: []
25
26#### Empty array with :-
27empty=()
28argv.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
35set -o nounset
36empty=()
37argv.py "${empty[@]}"
38echo status=$?
39## STDOUT:
40[]
41status=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.
48f() {
49 local a=(1 '2 3')
50 argv.py "${a[0]}"
51}
52f
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
59array=('1 2' $(echo '3 4'))
60argv.py "${array[@]}"
61## stdout: ['1 2', '3', '4']
62
63#### space before ( in array initialization
64# NOTE: mksh accepts this, but bash doesn't
65a= (1 '2 3')
66echo $a
67## status: 2
68## OK mksh status: 0
69## OK mksh stdout: 1
70
71#### array over multiple lines
72a=(
731
74'2 3'
75)
76argv.py "${a[@]}"
77## stdout: ['1', '2 3']
78## status: 0
79
80#### array with invalid token
81a=(
821
83&
84'2 3'
85)
86argv.py "${a[@]}"
87## status: 2
88## OK mksh status: 1
89
90#### array with empty string
91empty=('')
92argv.py "${empty[@]}"
93## stdout: ['']
94
95#### Retrieve index
96a=(1 '2 3')
97argv.py "${a[1]}"
98## stdout: ['2 3']
99
100#### Retrieve out of bounds index
101a=(1 '2 3')
102argv.py "${a[3]}"
103## stdout: ['']
104
105#### Negative index
106a=(1 '2 3')
107argv.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
112a=(0 1 2 3 4)
113unset a[1]
114unset a[4]
115echo "${a[@]}"
116echo -1 ${a[-1]}
117echo -2 ${a[-2]}
118echo -3 ${a[-3]}
119echo -4 ${a[-4]}
120echo -5 ${a[-5]}
121
122a[-1]+=0 # append 0 on the end
123echo ${a[@]}
124(( a[-1] += 42 ))
125echo ${a[@]}
126
127## STDOUT:
1280 2 3
129-1 3
130-2 2
131-3
132-4 0
133-5
1340 2 30
1350 2 72
136## END
137## BUG mksh STDOUT:
1380 2 3
139-1
140-2
141-3
142-4
143-5
1440 2 3 0
1450 2 3 42
146## END
147
148#### Negative index and sparse array
149a=(0 1)
150unset 'a[-1]' # remove last element
151a+=(2 3)
152echo ${a[0]} $((a[0]))
153echo ${a[1]} $((a[1]))
154echo ${a[2]} $((a[2]))
155echo ${a[3]} $((a[3]))
156## STDOUT:
1570 0
1582 2
1593 3
1600
161## END
162## BUG mksh STDOUT:
1630 0
1641 1
1652 2
1663 3
167## END
168
169#### Length after unset
170a=(0 1 2 3)
171unset a[-1]
172echo len=${#a[@]}
173unset a[-1]
174echo len=${#a[@]}
175## STDOUT:
176len=3
177len=2
178## END
179## BUG mksh STDOUT:
180len=4
181len=4
182## END
183
184#### Retrieve index that is a variable
185a=(1 '2 3')
186i=1
187argv.py "${a[$i]}"
188## stdout: ['2 3']
189
190#### Retrieve index that is a variable without $
191a=(1 '2 3')
192i=5
193argv.py "${a[i-4]}"
194## stdout: ['2 3']
195
196#### Retrieve index that is a command sub
197a=(1 '2 3')
198argv.py "${a[$(echo 1)]}"
199## stdout: ['2 3']
200
201#### Retrieve array indices with ${!a}
202a=(1 '2 3')
203argv.py "${!a[@]}"
204## stdout: ['0', '1']
205
206#### Retrieve sparse array indices with ${!a}
207a=()
208(( a[99]=1 ))
209argv.py "${!a[@]}"
210## STDOUT:
211['99']
212## END
213
214#### ${!a[1]} is named ref in bash
215# mksh ignores it
216foo=bar
217a=('1 2' foo '2 3')
218argv.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
228a=(1 '2 3')
229argv.py "${!a}"
230
231## stdout: ['']
232## status: 0
233## BUG mksh stdout: ['a']
234## BUG mksh status: 0
235
236#### All elements unquoted
237a=(1 '2 3')
238argv.py ${a[@]}
239## stdout: ['1', '2', '3']
240
241#### All elements quoted
242a=(1 '2 3')
243argv.py "${a[@]}"
244## stdout: ['1', '2 3']
245
246#### $*
247a=(1 '2 3')
248argv.py ${a[*]}
249## stdout: ['1', '2', '3']
250
251#### "$*"
252a=(1 '2 3')
253argv.py "${a[*]}"
254## stdout: ['1 2 3']
255
256#### Interpolate array into array
257a=(1 '2 3')
258a=(0 "${a[@]}" '4 5')
259argv.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
267export PYTHONPATH
268
269PYTHONPATH=mystr # NOTE: in bash, this doesn't work afterward!
270printenv.py PYTHONPATH
271
272PYTHONPATH=(myarray)
273printenv.py PYTHONPATH
274
275PYTHONPATH=(a b c)
276printenv.py PYTHONPATH
277
278## status: 0
279## STDOUT:
280mystr
281None
282None
283## END
284
285#### strict_array prevents exporting array
286
287shopt -s strict_array
288
289export PYTHONPATH
290PYTHONPATH=(a b c)
291printenv.py PYTHONPATH
292
293## status: 1
294## STDOUT:
295## END
296
297## N-I bash/mksh status: 0
298## N-I bash/mksh STDOUT:
299None
300## END
301
302#### Arrays can't be used as env bindings
303# Hm bash it treats it as a string!
304A=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
312a=(1 '2 3')
313a[0]=9
314argv.py "${a[@]}"
315## stdout: ['9', '2 3']
316
317#### Set element with var ref
318a=(1 '2 3')
319i=0
320a[$i]=9
321argv.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 [].
327a=(1 '2 3')
328i=(0 1)
329a[${i[1]}]=9
330argv.py "${a[@]}"
331## stdout: ['1', '9']
332
333#### Set array item to array
334a=(1 2)
335a[0]=(3 4)
336echo "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.
345a=(1 2 3)
346argv.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.
355a=(1 2 3 4 5)
356argv.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
362a=(1 2 3 4 5)
363argv.py "${a[@]: 1: -3}"
364## status: 1
365## stdout-json: ""
366
367#### Slice with arithmetic
368a=(1 2 3)
369i=5
370argv.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
376a=(1 '2 3')
377echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
378## stdout: 2 2
379
380#### Length of an element
381a=(1 '2 3')
382echo "${#a[1]}"
383## stdout: 3
384
385#### Iteration
386a=(1 '2 3')
387for v in "${a[@]}"; do
388 echo $v
389done
390## stdout-json: "1\n2 3\n"
391
392#### glob within array yields separate elements
393touch _tmp/y.Y _tmp/yy.Y
394a=(_tmp/*.Y)
395argv.py "${a[@]}"
396## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
397
398#### declare array and then append
399declare -a array
400array+=(a)
401array+=(b c)
402argv.py "${array[@]}"
403## stdout: ['a', 'b', 'c']
404
405#### Array syntax in wrong place
406ls 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.
413single=('')
414argv.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.
421files=('foo.c' 'sp ace.h' 'bar.c')
422argv.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
429files=('foo.c' 'sp ace.h' 'bar.c')
430argv.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.
439a=('123' '456')
440argv.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
446a=('123' '456')
447echo "${#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
453s='abc'
454echo ${s[@]}
455echo ${s[*]}
456## STDOUT:
457abc
458abc
459## END
460
461#### ${mystr[@]} and ${mystr[*]} disallowed with strict_array
462
463$SH -c 'shopt -s strict_array; s="abc"; echo ${s[@]}'
464echo status=$?
465
466$SH -c 'shopt -s strict_array; s="abc"; echo ${s[*]}'
467echo status=$?
468
469## status: 0
470## STDOUT:
471status=1
472status=1
473## END
474## N-I bash/mksh STDOUT:
475abc
476status=0
477abc
478status=0
479## END
480
481#### Create a "user" array out of the argv array
482set -- 'a b' 'c'
483array1=('x y' 'z')
484array2=("$@")
485argv.py "${array1[@]}" "${array2[@]}"
486## stdout: ['x y', 'z', 'a b', 'c']
487
488#### Tilde expansion within array
489HOME=/home/bob
490a=(~/src ~/git)
491echo "${a[@]}"
492## stdout: /home/bob/src /home/bob/git
493
494#### Brace Expansion within Array
495a=(-{a,b} {c,d}-)
496echo "${a[@]}"
497## stdout: -a -b c- d-
498
499#### array default
500default=('1 2' '3')
501argv.py "${undef[@]:-${default[@]}}"
502## stdout: ['1 2', '3']
503
504#### Singleton Array Copy and Assign. OSH can't index strings with ints
505a=( '12 3' )
506b=( "${a[@]}" )
507c="${a[@]}" # This decays it to a string
508d=${a[*]} # This decays it to a string
509echo ${#a[0]} ${#b[0]}
510echo ${#a[@]} ${#b[@]}
511
512# osh is intentionally stricter, and these fail.
513echo ${#c[0]} ${#d[0]}
514echo ${#c[@]} ${#d[@]}
515
516## status: 1
517## STDOUT:
5184 4
5191 1
520## END
521## OK bash/mksh status: 0
522## OK bash/mksh STDOUT:
5234 4
5241 1
5254 4
5261 1
527## END
528
529#### declare -a / local -a is empty array
530declare -a myarray
531argv.py "${myarray[@]}"
532myarray+=('x')
533argv.py "${myarray[@]}"
534
535f() {
536 local -a myarray
537 argv.py "${myarray[@]}"
538 myarray+=('x')
539 argv.py "${myarray[@]}"
540}
541f
542## STDOUT:
543[]
544['x']
545[]
546['x']
547## END
548
549#### Create sparse array
550a=()
551(( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
552echo len=${#a[@]}
553argv.py "${a[@]}"
554echo "unset=${a[33]}"
555echo len-of-unset=${#a[33]}
556## STDOUT:
557len=1
558['1']
559unset=
560len-of-unset=0
561## END
562
563#### Create sparse array implicitly
564(( a[99]=1 ))
565echo len=${#a[@]}
566argv.py "${a[@]}"
567echo "unset=${a[33]}"
568echo len-of-unset=${#a[33]}
569## STDOUT:
570len=1
571['1']
572unset=
573len-of-unset=0
574## END
575
576#### Append sparse arrays
577a=()
578(( a[99]=1 ))
579b=()
580(( b[33]=2 ))
581(( b[66]=3 ))
582a+=( "${b[@]}" )
583argv.py "${a[@]}"
584argv.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 ))
595argv.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
601shopt -u strict_arith
602a[a]=42
603a[a]=99
604argv.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
612shopt -u strict_arith
613a=(1 2 3)
614(( x = a[a] ))
615echo $x
616## status: 0
617## STDOUT:
6182
619## END
620
621#### a[$x$y] on LHS and RHS
622x=1
623y=2
624a[$x$y]=foo
625
626# not allowed by OSH parsing
627#echo ${a[$x$y]}
628
629echo ${a[12]}
630echo ${#a[@]}
631
632## STDOUT:
633foo
6341
635## END
636
637
638#### Dynamic parsing of LHS a[$code]=value
639
640declare -a array
641array[x=1]='one'
642
643code='y=2'
644#code='1+2' # doesn't work either
645array[$code]='two'
646
647argv.py "${array[@]}"
648echo x=$x
649echo y=$y
650
651## STDOUT:
652['one', 'two']
653x=1
654y=2
655## END
656## N-I dash stdout-json: ""
657## N-I dash status: 2
658
659#### Dynamic parsing of RHS ${a[$code]}
660declare -a array
661array=(zero one two three)
662
663echo ${array[1+2]}
664
665code='1+2'
666echo ${array[$code]}
667
668## STDOUT:
669three
670three
671## END
672
673# it still dynamically parses
674
675## OK zsh STDOUT:
676two
677two
678## END