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

627 lines, 289 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 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.
34set -o nounset
35empty=()
36argv.py "${empty[@]}"
37echo status=$?
38## STDOUT:
39[]
40status=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.
47f() {
48 local a=(1 '2 3')
49 argv.py "${a[0]}"
50}
51f
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
58array=('1 2' $(echo '3 4'))
59argv.py "${array[@]}"
60## stdout: ['1 2', '3', '4']
61
62#### space before ( in array initialization
63# NOTE: mksh accepts this, but bash doesn't
64a= (1 '2 3')
65echo $a
66## status: 2
67## OK mksh status: 0
68## OK mksh stdout: 1
69
70#### array over multiple lines
71a=(
721
73'2 3'
74)
75argv.py "${a[@]}"
76## stdout: ['1', '2 3']
77## status: 0
78
79#### array with invalid token
80a=(
811
82&
83'2 3'
84)
85argv.py "${a[@]}"
86## status: 2
87## OK mksh status: 1
88
89#### array with empty string
90empty=('')
91argv.py "${empty[@]}"
92## stdout: ['']
93
94#### Retrieve index
95a=(1 '2 3')
96argv.py "${a[1]}"
97## stdout: ['2 3']
98
99#### Retrieve out of bounds index
100a=(1 '2 3')
101argv.py "${a[3]}"
102## stdout: ['']
103
104#### Negative index
105a=(1 '2 3')
106argv.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
111a=(0 1 2 3 4)
112unset a[1]
113unset a[4]
114echo "${a[@]}"
115echo -1 ${a[-1]}
116echo -2 ${a[-2]}
117echo -3 ${a[-3]}
118echo -4 ${a[-4]}
119echo -5 ${a[-5]}
120
121a[-1]+=0 # append 0 on the end
122echo ${a[@]}
123(( a[-1] += 42 ))
124echo ${a[@]}
125
126## STDOUT:
1270 2 3
128-1 3
129-2 2
130-3
131-4 0
132-5
1330 2 30
1340 2 72
135## END
136## BUG mksh STDOUT:
1370 2 3
138-1
139-2
140-3
141-4
142-5
1430 2 3 0
1440 2 3 42
145## END
146
147#### Negative index and sparse array
148a=(0 1)
149unset 'a[-1]' # remove last element
150a+=(2 3)
151echo ${a[0]} $((a[0]))
152echo ${a[1]} $((a[1]))
153echo ${a[2]} $((a[2]))
154echo ${a[3]} $((a[3]))
155## STDOUT:
1560 0
1572 2
1583 3
1590
160## END
161## BUG mksh STDOUT:
1620 0
1631 1
1642 2
1653 3
166## END
167
168#### Length after unset
169a=(0 1 2 3)
170unset a[-1]
171echo len=${#a[@]}
172unset a[-1]
173echo len=${#a[@]}
174## STDOUT:
175len=3
176len=2
177## END
178## BUG mksh STDOUT:
179len=4
180len=4
181## END
182
183#### Retrieve index that is a variable
184a=(1 '2 3')
185i=1
186argv.py "${a[$i]}"
187## stdout: ['2 3']
188
189#### Retrieve index that is a variable without $
190a=(1 '2 3')
191i=5
192argv.py "${a[i-4]}"
193## stdout: ['2 3']
194
195#### Retrieve index that is a command sub
196a=(1 '2 3')
197argv.py "${a[$(echo 1)]}"
198## stdout: ['2 3']
199
200#### Retrieve array indices with ${!a}
201a=(1 '2 3')
202argv.py "${!a[@]}"
203## stdout: ['0', '1']
204
205#### Retrieve sparse array indices with ${!a}
206a=()
207(( a[99]=1 ))
208argv.py "${!a[@]}"
209## STDOUT:
210['99']
211## END
212
213#### ${!a[1]} is named ref in bash
214# mksh ignores it
215foo=bar
216a=('1 2' foo '2 3')
217argv.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
227a=(1 '2 3')
228argv.py "${!a}"
229
230## stdout: ['']
231## status: 0
232## BUG mksh stdout: ['a']
233## BUG mksh status: 0
234
235#### All elements unquoted
236a=(1 '2 3')
237argv.py ${a[@]}
238## stdout: ['1', '2', '3']
239
240#### All elements quoted
241a=(1 '2 3')
242argv.py "${a[@]}"
243## stdout: ['1', '2 3']
244
245#### $*
246a=(1 '2 3')
247argv.py ${a[*]}
248## stdout: ['1', '2', '3']
249
250#### "$*"
251a=(1 '2 3')
252argv.py "${a[*]}"
253## stdout: ['1 2 3']
254
255#### Interpolate array into array
256a=(1 '2 3')
257a=(0 "${a[@]}" '4 5')
258argv.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.
265export PYTHONPATH=(a b c)
266export PYTHONPATH=a # NOTE: in bash, this doesn't work afterward!
267printenv.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!
275A=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
283a=(1 '2 3')
284a[0]=9
285argv.py "${a[@]}"
286## stdout: ['9', '2 3']
287
288#### Set element with var ref
289a=(1 '2 3')
290i=0
291a[$i]=9
292argv.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 [].
298a=(1 '2 3')
299i=(0 1)
300a[${i[1]}]=9
301argv.py "${a[@]}"
302## stdout: ['1', '9']
303
304#### Set array item to array
305a=(1 2)
306a[0]=(3 4)
307echo "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.
316a=(1 2 3)
317argv.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.
326a=(1 2 3 4 5)
327argv.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
333a=(1 2 3 4 5)
334argv.py "${a[@]: 1: -3}"
335## status: 1
336## stdout-json: ""
337
338#### Slice with arithmetic
339a=(1 2 3)
340i=5
341argv.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
347a=(1 '2 3')
348echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
349## stdout: 2 2
350
351#### Length of an element
352a=(1 '2 3')
353echo "${#a[1]}"
354## stdout: 3
355
356#### Iteration
357a=(1 '2 3')
358for v in "${a[@]}"; do
359 echo $v
360done
361## stdout-json: "1\n2 3\n"
362
363#### glob within array yields separate elements
364touch _tmp/y.Y _tmp/yy.Y
365a=(_tmp/*.Y)
366argv.py "${a[@]}"
367## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
368
369#### declare array and then append
370declare -a array
371array+=(a)
372array+=(b c)
373argv.py "${array[@]}"
374## stdout: ['a', 'b', 'c']
375
376#### Array syntax in wrong place
377ls 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.
384single=('')
385argv.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.
392files=('foo.c' 'sp ace.h' 'bar.c')
393argv.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
400files=('foo.c' 'sp ace.h' 'bar.c')
401argv.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.
410a=('123' '456')
411argv.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
417a=('123' '456')
418echo "${#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
424s='abc'
425echo ${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
431set -- 'a b' 'c'
432array1=('x y' 'z')
433array2=("$@")
434argv.py "${array1[@]}" "${array2[@]}"
435## stdout: ['x y', 'z', 'a b', 'c']
436
437#### Tilde expansion within array
438HOME=/home/bob
439a=(~/src ~/git)
440echo "${a[@]}"
441## stdout: /home/bob/src /home/bob/git
442
443#### Brace Expansion within Array
444a=(-{a,b} {c,d}-)
445echo "${a[@]}"
446## stdout: -a -b c- d-
447
448#### array default
449default=('1 2' '3')
450argv.py "${undef[@]:-${default[@]}}"
451## stdout: ['1 2', '3']
452
453#### Singleton Array Copy and Assign. OSH can't index strings with ints
454a=( '12 3' )
455b=( "${a[@]}" )
456c="${a[@]}" # This decays it to a string
457d=${a[*]} # This decays it to a string
458echo ${#a[0]} ${#b[0]}
459echo ${#a[@]} ${#b[@]}
460
461# osh is intentionally stricter, and these fail.
462echo ${#c[0]} ${#d[0]}
463echo ${#c[@]} ${#d[@]}
464
465## status: 1
466## STDOUT:
4674 4
4681 1
469## END
470## OK bash/mksh status: 0
471## OK bash/mksh STDOUT:
4724 4
4731 1
4744 4
4751 1
476## END
477
478#### declare -a / local -a is empty array
479declare -a myarray
480argv.py "${myarray[@]}"
481myarray+=('x')
482argv.py "${myarray[@]}"
483
484f() {
485 local -a myarray
486 argv.py "${myarray[@]}"
487 myarray+=('x')
488 argv.py "${myarray[@]}"
489}
490f
491## STDOUT:
492[]
493['x']
494[]
495['x']
496## END
497
498#### Create sparse array
499a=()
500(( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
501echo len=${#a[@]}
502argv.py "${a[@]}"
503echo "unset=${a[33]}"
504echo len-of-unset=${#a[33]}
505## STDOUT:
506len=1
507['1']
508unset=
509len-of-unset=0
510## END
511
512#### Create sparse array implicitly
513(( a[99]=1 ))
514echo len=${#a[@]}
515argv.py "${a[@]}"
516echo "unset=${a[33]}"
517echo len-of-unset=${#a[33]}
518## STDOUT:
519len=1
520['1']
521unset=
522len-of-unset=0
523## END
524
525#### Append sparse arrays
526a=()
527(( a[99]=1 ))
528b=()
529(( b[33]=2 ))
530(( b[66]=3 ))
531a+=( "${b[@]}" )
532argv.py "${a[@]}"
533argv.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 ))
544argv.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
550shopt -u strict_arith
551a[a]=42
552a[a]=99
553argv.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
561shopt -u strict_arith
562a=(1 2 3)
563(( x = a[a] ))
564echo $x
565## status: 0
566## STDOUT:
5672
568## END
569
570#### a[$x$y] on LHS and RHS
571x=1
572y=2
573a[$x$y]=foo
574
575# not allowed by OSH parsing
576#echo ${a[$x$y]}
577
578echo ${a[12]}
579echo ${#a[@]}
580
581## STDOUT:
582foo
5831
584## END
585
586
587#### Dynamic parsing of LHS a[$code]=value
588
589declare -a array
590array[x=1]='one'
591
592code='y=2'
593#code='1+2' # doesn't work either
594array[$code]='two'
595
596argv.py "${array[@]}"
597echo x=$x
598echo y=$y
599
600## STDOUT:
601['one', 'two']
602x=1
603y=2
604## END
605## N-I dash stdout-json: ""
606## N-I dash status: 2
607
608#### Dynamic parsing of RHS ${a[$code]}
609declare -a array
610array=(zero one two three)
611
612echo ${array[1+2]}
613
614code='1+2'
615echo ${array[$code]}
616
617## STDOUT:
618three
619three
620## END
621
622# it still dynamically parses
623
624## OK zsh STDOUT:
625two
626two
627## END