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

803 lines, 383 significant
1## compare_shells: bash mksh
2## oils_failures_allowed: 3
3
4#### nounset / set -u with empty array (bug in bash 4.3, fixed in 4.4)
5
6# http://lists.gnu.org/archive/html/help-bash/2017-09/msg00005.html
7
8set -o nounset
9empty=()
10argv.py "${empty[@]}"
11echo status=$?
12## STDOUT:
13[]
14status=0
15## END
16## BUG mksh stdout-json: ""
17## BUG mksh status: 1
18
19#### local array
20# mksh support local variables, but not local arrays, oddly.
21f() {
22 local a=(1 '2 3')
23 argv.py "${a[0]}"
24}
25f
26## stdout: ['1']
27## status: 0
28## BUG mksh status: 1
29## BUG mksh stdout-json: ""
30
31#### Command with with word splitting in array
32array=('1 2' $(echo '3 4'))
33argv.py "${array[@]}"
34## stdout: ['1 2', '3', '4']
35
36#### space before ( in array initialization
37# NOTE: mksh accepts this, but bash doesn't
38a= (1 '2 3')
39echo $a
40## status: 2
41## OK mksh status: 0
42## OK mksh stdout: 1
43
44#### array over multiple lines
45a=(
461
47'2 3'
48)
49argv.py "${a[@]}"
50## stdout: ['1', '2 3']
51## status: 0
52
53#### array with invalid token
54a=(
551
56&
57'2 3'
58)
59argv.py "${a[@]}"
60## status: 2
61## OK mksh status: 1
62
63#### array with empty string
64empty=('')
65argv.py "${empty[@]}"
66## stdout: ['']
67
68#### Retrieve index
69a=(1 '2 3')
70argv.py "${a[1]}"
71## stdout: ['2 3']
72
73#### Retrieve out of bounds index
74a=(1 '2 3')
75argv.py "${a[3]}"
76## stdout: ['']
77
78#### Negative index
79a=(1 '2 3')
80argv.py "${a[-1]}" "${a[-2]}" "${a[-5]}" # last one out of bounds
81## stdout: ['2 3', '1', '']
82## N-I mksh stdout: ['', '', '']
83
84#### Negative index and sparse array
85a=(0 1 2 3 4)
86unset a[1]
87unset a[4]
88echo "${a[@]}"
89echo -1 ${a[-1]}
90echo -2 ${a[-2]}
91echo -3 ${a[-3]}
92echo -4 ${a[-4]}
93echo -5 ${a[-5]}
94
95a[-1]+=0 # append 0 on the end
96echo ${a[@]}
97(( a[-1] += 42 ))
98echo ${a[@]}
99
100## STDOUT:
1010 2 3
102-1 3
103-2 2
104-3
105-4 0
106-5
1070 2 30
1080 2 72
109## END
110## BUG mksh STDOUT:
1110 2 3
112-1
113-2
114-3
115-4
116-5
1170 2 3 0
1180 2 3 42
119## END
120
121#### Negative index and sparse array
122a=(0 1)
123unset 'a[-1]' # remove last element
124a+=(2 3)
125echo ${a[0]} $((a[0]))
126echo ${a[1]} $((a[1]))
127echo ${a[2]} $((a[2]))
128echo ${a[3]} $((a[3]))
129## STDOUT:
1300 0
1312 2
1323 3
1330
134## END
135## BUG mksh STDOUT:
1360 0
1371 1
1382 2
1393 3
140## END
141
142#### Length after unset
143a=(0 1 2 3)
144unset a[-1]
145echo len=${#a[@]}
146unset a[-1]
147echo len=${#a[@]}
148## STDOUT:
149len=3
150len=2
151## END
152## BUG mksh STDOUT:
153len=4
154len=4
155## END
156
157#### Retrieve index that is a variable
158a=(1 '2 3')
159i=1
160argv.py "${a[$i]}"
161## stdout: ['2 3']
162
163#### Retrieve index that is a variable without $
164a=(1 '2 3')
165i=5
166argv.py "${a[i-4]}"
167## stdout: ['2 3']
168
169#### Retrieve index that is a command sub
170a=(1 '2 3')
171argv.py "${a[$(echo 1)]}"
172## stdout: ['2 3']
173
174#### Retrieve array indices with ${!a}
175a=(1 '2 3')
176argv.py "${!a[@]}"
177## stdout: ['0', '1']
178
179#### Retrieve sparse array indices with ${!a}
180a=()
181(( a[99]=1 ))
182argv.py "${!a[@]}"
183## STDOUT:
184['99']
185## END
186
187#### ${!a[1]} is named ref in bash
188# mksh ignores it
189foo=bar
190a=('1 2' foo '2 3')
191argv.py "${!a[1]}"
192## status: 0
193## stdout: ['bar']
194## N-I mksh stdout: ['a[1]']
195
196#### ${!a} on array
197
198# bash gives empty string because it's like a[0]
199# mksh gives the name of the variable with !. Very weird.
200
201a=(1 '2 3')
202argv.py "${!a}"
203
204## stdout: ['']
205## status: 0
206## BUG mksh stdout: ['a']
207## BUG mksh status: 0
208
209#### All elements unquoted
210a=(1 '2 3')
211argv.py ${a[@]}
212## stdout: ['1', '2', '3']
213
214#### All elements quoted
215a=(1 '2 3')
216argv.py "${a[@]}"
217## stdout: ['1', '2 3']
218
219#### $*
220a=(1 '2 3')
221argv.py ${a[*]}
222## stdout: ['1', '2', '3']
223
224#### "$*"
225a=(1 '2 3')
226argv.py "${a[*]}"
227## stdout: ['1 2 3']
228
229#### Interpolate array into array
230a=(1 '2 3')
231a=(0 "${a[@]}" '4 5')
232argv.py "${a[@]}"
233## stdout: ['0', '1', '2 3', '4 5']
234
235#### Exporting array doesn't do anything, not even first element
236# bash parses, but doesn't execute.
237# mksh gives syntax error -- parses differently with 'export'
238# osh no longer parses this statically.
239
240export PYTHONPATH
241
242PYTHONPATH=mystr # NOTE: in bash, this doesn't work afterward!
243printenv.py PYTHONPATH
244
245PYTHONPATH=(myarray)
246printenv.py PYTHONPATH
247
248PYTHONPATH=(a b c)
249printenv.py PYTHONPATH
250
251## status: 0
252## STDOUT:
253mystr
254None
255None
256## END
257
258#### strict_array prevents exporting array
259
260shopt -s strict_array
261
262export PYTHONPATH
263PYTHONPATH=(a b c)
264printenv.py PYTHONPATH
265
266## status: 1
267## STDOUT:
268## END
269
270## N-I bash/mksh status: 0
271## N-I bash/mksh STDOUT:
272None
273## END
274
275#### Arrays can't be used as env bindings
276# Hm bash it treats it as a string!
277A=a B=(b b) printenv.py A B
278## status: 2
279## stdout-json: ""
280## OK bash stdout-json: "a\n(b b)\n"
281## OK bash status: 0
282## OK mksh status: 1
283
284#### Set element
285a=(1 '2 3')
286a[0]=9
287argv.py "${a[@]}"
288## stdout: ['9', '2 3']
289
290#### Set element with var ref
291a=(1 '2 3')
292i=0
293a[$i]=9
294argv.py "${a[@]}"
295## stdout: ['9', '2 3']
296
297#### Set element with array ref
298# This makes parsing a little more complex. Anything can be inside [],
299# including other [].
300a=(1 '2 3')
301i=(0 1)
302a[${i[1]}]=9
303argv.py "${a[@]}"
304## stdout: ['1', '9']
305
306#### Set array item to array
307a=(1 2)
308a[0]=(3 4)
309echo "status=$?"
310## stdout-json: ""
311## status: 2
312## N-I mksh status: 1
313## BUG bash stdout: status=1
314## BUG bash status: 0
315
316#### Slice of array with [@]
317# mksh doesn't support this syntax! It's a bash extension.
318a=(1 2 3)
319argv.py "${a[@]:1:2}"
320## stdout: ['2', '3']
321## N-I mksh status: 1
322## N-I mksh stdout-json: ""
323
324#### Negative slice begin
325# mksh doesn't support this syntax! It's a bash extension.
326# NOTE: for some reason -2) has to be in parens? Ah that's because it
327# conflicts with :-! That's silly. You can also add a space.
328a=(1 2 3 4 5)
329argv.py "${a[@]:(-4)}"
330## stdout: ['2', '3', '4', '5']
331## N-I mksh status: 1
332## N-I mksh stdout-json: ""
333
334#### Negative slice length
335a=(1 2 3 4 5)
336argv.py "${a[@]: 1: -3}"
337## status: 1
338## stdout-json: ""
339
340#### Slice with arithmetic
341a=(1 2 3)
342i=5
343argv.py "${a[@]:i-4:2}"
344## stdout: ['2', '3']
345## N-I mksh status: 1
346## N-I mksh stdout-json: ""
347
348#### Number of elements
349a=(1 '2 3')
350echo "${#a[@]}" ${#a[@]} # bug fix: also test without quotes
351## stdout: 2 2
352
353#### Length of an element
354a=(1 '2 3')
355echo "${#a[1]}"
356## stdout: 3
357
358#### Iteration
359a=(1 '2 3')
360for v in "${a[@]}"; do
361 echo $v
362done
363## stdout-json: "1\n2 3\n"
364
365#### glob within array yields separate elements
366touch _tmp/y.Y _tmp/yy.Y
367a=(_tmp/*.Y)
368argv.py "${a[@]}"
369## stdout: ['_tmp/y.Y', '_tmp/yy.Y']
370
371#### declare array and then append
372declare -a array
373array+=(a)
374array+=(b c)
375argv.py "${array[@]}"
376## stdout: ['a', 'b', 'c']
377
378#### Array syntax in wrong place
379ls foo=(1 2)
380## status: 1
381## OK bash status: 2
382
383#### Single array with :-
384
385# 2024-06 - bash 5.2 and mksh now match, bash 4.4 differed.
386# Could change OSH
387# zsh agrees with OSH, but it fails most test cases
388
389single=('')
390argv.py ${single[@]:-none} x "${single[@]:-none}"
391## stdout: ['none', 'x', 'none']
392
393#### Stripping a whole array unquoted
394# Problem: it joins it first.
395files=('foo.c' 'sp ace.h' 'bar.c')
396argv.py ${files[@]%.c}
397## status: 0
398## stdout: ['foo', 'sp', 'ace.h', 'bar']
399## N-I mksh status: 1
400## N-I mksh stdout-json: ""
401
402#### Stripping a whole array quoted
403files=('foo.c' 'sp ace.h' 'bar.c')
404argv.py "${files[@]%.c}"
405## status: 0
406## stdout: ['foo', 'sp ace.h', 'bar']
407## N-I mksh status: 1
408## N-I mksh stdout-json: ""
409
410#### Multiple subscripts not allowed
411# NOTE: bash 4.3 had a bug where it ignored the bad subscript, but now it is
412# fixed.
413a=('123' '456')
414argv.py "${a[0]}" "${a[0][0]}"
415## stdout-json: ""
416## status: 2
417## OK bash/mksh status: 1
418
419#### Length op, index op, then transform op is not allowed
420a=('123' '456')
421echo "${#a[0]}" "${#a[0]/1/xxx}"
422## stdout-json: ""
423## status: 2
424## OK bash/mksh status: 1
425
426#### ${mystr[@]} and ${mystr[*]} are no-ops
427s='abc'
428echo ${s[@]}
429echo ${s[*]}
430## STDOUT:
431abc
432abc
433## END
434
435#### ${mystr[@]} and ${mystr[*]} disallowed with strict_array
436
437$SH -c 'shopt -s strict_array; s="abc"; echo ${s[@]}'
438echo status=$?
439
440$SH -c 'shopt -s strict_array; s="abc"; echo ${s[*]}'
441echo status=$?
442
443## status: 0
444## STDOUT:
445status=1
446status=1
447## END
448## N-I bash/mksh STDOUT:
449abc
450status=0
451abc
452status=0
453## END
454
455#### Create a "user" array out of the argv array
456set -- 'a b' 'c'
457array1=('x y' 'z')
458array2=("$@")
459argv.py "${array1[@]}" "${array2[@]}"
460## stdout: ['x y', 'z', 'a b', 'c']
461
462#### Tilde expansion within array
463HOME=/home/bob
464a=(~/src ~/git)
465echo "${a[@]}"
466## stdout: /home/bob/src /home/bob/git
467
468#### Brace Expansion within Array
469a=(-{a,b} {c,d}-)
470echo "${a[@]}"
471## stdout: -a -b c- d-
472
473#### array default
474default=('1 2' '3')
475argv.py "${undef[@]:-${default[@]}}"
476## stdout: ['1 2', '3']
477
478#### Singleton Array Copy and Assign. OSH can't index strings with ints
479a=( '12 3' )
480b=( "${a[@]}" )
481c="${a[@]}" # This decays it to a string
482d=${a[*]} # This decays it to a string
483echo ${#a[0]} ${#b[0]}
484echo ${#a[@]} ${#b[@]}
485
486# osh is intentionally stricter, and these fail.
487echo ${#c[0]} ${#d[0]}
488echo ${#c[@]} ${#d[@]}
489
490## status: 1
491## STDOUT:
4924 4
4931 1
494## END
495## OK bash/mksh status: 0
496## OK bash/mksh STDOUT:
4974 4
4981 1
4994 4
5001 1
501## END
502
503#### declare -a / local -a is empty array
504declare -a myarray
505argv.py "${myarray[@]}"
506myarray+=('x')
507argv.py "${myarray[@]}"
508
509f() {
510 local -a myarray
511 argv.py "${myarray[@]}"
512 myarray+=('x')
513 argv.py "${myarray[@]}"
514}
515f
516## STDOUT:
517[]
518['x']
519[]
520['x']
521## END
522
523#### Create sparse array
524a=()
525(( a[99]=1 )) # osh doesn't parse index assignment outside arithmetic yet
526echo len=${#a[@]}
527argv.py "${a[@]}"
528echo "unset=${a[33]}"
529echo len-of-unset=${#a[33]}
530## STDOUT:
531len=1
532['1']
533unset=
534len-of-unset=0
535## END
536
537#### Create sparse array implicitly
538(( a[99]=1 ))
539echo len=${#a[@]}
540argv.py "${a[@]}"
541echo "unset=${a[33]}"
542echo len-of-unset=${#a[33]}
543## STDOUT:
544len=1
545['1']
546unset=
547len-of-unset=0
548## END
549
550#### Append sparse arrays
551a=()
552(( a[99]=1 ))
553b=()
554(( b[33]=2 ))
555(( b[66]=3 ))
556a+=( "${b[@]}" )
557argv.py "${a[@]}"
558argv.py "${a[99]}" "${a[100]}" "${a[101]}"
559## STDOUT:
560['1', '2', '3']
561['1', '2', '3']
562## END
563
564#### Slice of sparse array with [@]
565# mksh doesn't support this syntax! It's a bash extension.
566(( a[33]=1 ))
567(( a[66]=2 ))
568(( a[99]=2 ))
569argv.py "${a[@]:15:2}"
570## stdout: ['1', '2']
571## N-I mksh status: 1
572## N-I mksh stdout-json: ""
573
574#### Using an array itself as the index on LHS
575shopt -u strict_arith
576a[a]=42
577a[a]=99
578argv.py "${a[@]}" "${a[0]}" "${a[42]}" "${a[99]}"
579
580## status: 0
581## STDOUT:
582['42', '99', '42', '99', '']
583## END
584
585#### Using an array itself as the index on RHS
586shopt -u strict_arith
587a=(1 2 3)
588(( x = a[a] ))
589echo $x
590## status: 0
591## STDOUT:
5922
593## END
594
595#### a[$x$y] on LHS and RHS
596x=1
597y=2
598a[$x$y]=foo
599
600# not allowed by OSH parsing
601#echo ${a[$x$y]}
602
603echo ${a[12]}
604echo ${#a[@]}
605
606## STDOUT:
607foo
6081
609## END
610
611
612#### Dynamic parsing of LHS a[$code]=value
613
614declare -a array
615array[x=1]='one'
616
617code='y=2'
618#code='1+2' # doesn't work either
619array[$code]='two'
620
621argv.py "${array[@]}"
622echo x=$x
623echo y=$y
624
625## STDOUT:
626['one', 'two']
627x=1
628y=2
629## END
630## N-I dash stdout-json: ""
631## N-I dash status: 2
632
633#### Dynamic parsing of RHS ${a[$code]}
634declare -a array
635array=(zero one two three)
636
637echo ${array[1+2]}
638
639code='1+2'
640echo ${array[$code]}
641
642## STDOUT:
643three
644three
645## END
646
647# it still dynamically parses
648
649## OK zsh STDOUT:
650two
651two
652## END
653
654
655#### Is element set? test -v a[i]
656
657# note: modern versions of zsh implement this
658
659array=(1 2 3 '')
660
661test -v 'array[1]'
662echo set=$?
663
664test -v 'array[3]'
665echo empty=$?
666
667test -v 'array[4]'
668echo unset=$?
669
670## STDOUT:
671set=0
672empty=0
673unset=1
674## END
675
676## N-I mksh STDOUT:
677set=2
678empty=2
679unset=2
680## END
681
682
683#### [[ -v a[i] ]]
684
685# note: modern versions of zsh implement this
686
687array=(1 2 3)
688[[ -v array[1] ]]
689echo status=$?
690
691[[ -v array[4] ]]
692echo status=$?
693
694## STDOUT:
695status=0
696status=1
697## END
698
699## N-I mksh status: 1
700## N-I mksh STDOUT:
701## END
702
703
704#### test -v a[i] with arith expressions
705
706array=(1 2 3 '')
707
708test -v 'array[1+1]'
709echo status=$?
710
711test -v 'array[4+1]'
712echo status=$?
713
714echo
715echo dbracket
716
717[[ -v array[1+1] ]]
718echo status=$?
719
720[[ -v array[4+1] ]]
721echo status=$?
722
723## STDOUT:
724status=0
725status=1
726
727dbracket
728status=0
729status=1
730## END
731
732## N-I mksh status: 1
733## N-I mksh STDOUT:
734status=2
735status=2
736
737dbracket
738## END
739
740
741#### More arith expressions in [[ -v array[expr]] ]]
742
743typeset -a array
744array=('' nonempty)
745
746# This feels inconsistent with the rest of bash?
747zero=0
748
749[[ -v array[zero+0] ]]
750echo zero=$?
751
752[[ -v array[zero+1] ]]
753echo one=$?
754
755[[ -v array[zero+2] ]]
756echo two=$?
757
758echo ---
759
760i='0+0'
761[[ -v array[i] ]]
762echo zero=$?
763
764i='0+1'
765[[ -v array[i] ]]
766echo one=$?
767
768i='0+2'
769[[ -v array[i] ]]
770echo two=$?
771
772echo ---
773
774i='0+0'
775[[ -v array[$i] ]]
776echo zero=$?
777
778i='0+1'
779[[ -v array[$i] ]]
780echo one=$?
781
782i='0+2'
783[[ -v array[$i] ]]
784echo two=$?
785
786
787## STDOUT:
788zero=0
789one=0
790two=1
791---
792zero=0
793one=0
794two=1
795---
796zero=0
797one=0
798two=1
799## END
800
801## N-I mksh status: 1
802## N-I mksh STDOUT:
803## END