OILS / spec / assign-extended.test.sh View on Github | oils.pub

921 lines, 572 significant
1## compare_shells: bash-4.4 mksh
2
3# Extended assignment language, e.g. typeset, declare, arrays, etc.
4# Things that dash doesn't support.
5
6#### local -a
7# nixpkgs setup.sh uses this (issue #26)
8f() {
9 local -a array=(x y z)
10 argv.py "${array[@]}"
11}
12f
13## stdout: ['x', 'y', 'z']
14## N-I mksh stdout-json: ""
15## N-I mksh status: 1
16
17#### declare -a
18# nixpkgs setup.sh uses this (issue #26)
19declare -a array=(x y z)
20argv.py "${array[@]}"
21## stdout: ['x', 'y', 'z']
22## N-I mksh stdout-json: ""
23## N-I mksh status: 1
24
25#### indexed LHS with spaces (not allowed in OSH)
26a[1 * 1]=x a[ 1 + 2 ]=z
27echo status=$?
28argv.py "${a[@]}"
29## STDOUT:
30status=0
31['x', 'z']
32## END
33## N-I osh STDOUT:
34status=127
35[]
36## END
37
38#### declare -f exit code indicates function existence
39func2=x # var names are NOT found
40declare -f myfunc func2
41echo $?
42
43myfunc() { echo myfunc; }
44# This prints the source code.
45declare -f myfunc func2 > /dev/null
46echo $?
47
48func2() { echo func2; }
49declare -f myfunc func2 > /dev/null
50echo $?
51## STDOUT:
521
531
540
55## END
56## N-I mksh STDOUT:
57127
58127
59127
60## END
61
62#### declare -F prints function names
63add () { expr 4 + 4; }
64div () { expr 6 / 2; }
65ek () { echo hello; }
66__ec () { echo hi; }
67_ab () { expr 10 % 3; }
68
69declare -F
70## STDOUT:
71declare -f __ec
72declare -f _ab
73declare -f add
74declare -f div
75declare -f ek
76## END
77## N-I mksh stdout-json: ""
78## N-I mksh status: 127
79
80#### declare -F with shopt -s extdebug prints more info
81case $SH in mksh) exit ;; esac
82
83source $REPO_ROOT/spec/testdata/bash-source-2.sh
84
85shopt -s extdebug
86
87add () { expr 4 + 4; }
88
89declare -F
90echo
91
92declare -F add
93# in bash-source-2
94declare -F g | sed "s;$REPO_ROOT;ROOT;g"
95
96## STDOUT:
97declare -f add
98declare -f g
99
100add 7 main
101g 3 ROOT/spec/testdata/bash-source-2.sh
102## END
103## N-I mksh STDOUT:
104## END
105
106#### declare -F with shopt -s extdebug and main file
107case $SH in mksh) exit ;; esac
108
109$SH $REPO_ROOT/spec/testdata/extdebug.sh | sed "s;$REPO_ROOT;ROOT;g"
110
111## STDOUT:
112declare -f add
113declare -f g
114
115add 5 ROOT/spec/testdata/extdebug.sh
116g 3 ROOT/spec/testdata/bash-source-2.sh
117## END
118## N-I mksh STDOUT:
119## END
120
121#### declare -p var (exit status)
122var1() { echo func; } # function names are NOT found.
123declare -p var1 var2 >/dev/null
124echo $?
125
126var1=x
127declare -p var1 var2 >/dev/null
128echo $?
129
130var2=y
131declare -p var1 var2 >/dev/null
132echo $?
133## STDOUT:
1341
1351
1360
137## N-I mksh STDOUT:
138127
139127
140127
141## END
142
143#### declare
144test_var1=111
145readonly test_var2=222
146export test_var3=333
147declare -n test_var4=test_var1
148f1() {
149 local test_var5=555
150 {
151 echo '[declare]'
152 declare
153 echo '[readonly]'
154 readonly
155 echo '[export]'
156 export
157 echo '[local]'
158 local
159 } | grep -E '^\[|^\b.*test_var.\b'
160}
161f1
162## STDOUT:
163[declare]
164test_var1=111
165test_var2=222
166test_var3=333
167test_var4=test_var1
168test_var5=555
169[readonly]
170declare -r test_var2=222
171[export]
172declare -x test_var3=333
173[local]
174test_var5=555
175## END
176## OK bash STDOUT:
177[declare]
178test_var1=111
179test_var2=222
180test_var3=333
181test_var4=test_var1
182test_var5=555
183[readonly]
184declare -r test_var2="222"
185[export]
186declare -x test_var3="333"
187[local]
188test_var5=555
189## END
190## N-I mksh STDOUT:
191[declare]
192[readonly]
193test_var2
194[export]
195test_var3
196[local]
197typeset test_var1
198typeset -r test_var2
199typeset -x test_var3
200typeset test_var5
201## END
202
203#### declare -p
204# BUG: bash doesn't output flags with "local -p", which seems to contradict
205# with manual.
206test_var1=111
207readonly test_var2=222
208export test_var3=333
209declare -n test_var4=test_var1
210f1() {
211 local test_var5=555
212 {
213 echo '[declare]'
214 declare -p
215 echo '[readonly]'
216 readonly -p
217 echo '[export]'
218 export -p
219 echo '[local]'
220 local -p
221 } | grep -E '^\[|^\b.*test_var.\b'
222}
223f1
224## STDOUT:
225[declare]
226declare -- test_var1=111
227declare -r test_var2=222
228declare -x test_var3=333
229declare -n test_var4=test_var1
230declare -- test_var5=555
231[readonly]
232declare -r test_var2=222
233[export]
234declare -x test_var3=333
235[local]
236declare -- test_var5=555
237## END
238## BUG bash STDOUT:
239[declare]
240declare -- test_var1="111"
241declare -r test_var2="222"
242declare -x test_var3="333"
243declare -n test_var4="test_var1"
244declare -- test_var5="555"
245[readonly]
246declare -r test_var2="222"
247[export]
248declare -x test_var3="333"
249[local]
250test_var5=555
251## END
252## N-I mksh STDOUT:
253[declare]
254[readonly]
255readonly test_var2=222
256[export]
257export test_var3=333
258[local]
259typeset test_var1=111
260typeset -r test_var2=222
261typeset -x test_var3=333
262typeset test_var5=555
263## END
264
265#### declare -p doesn't print binary data, but can be loaded into bash
266
267# bash prints binary data!
268case $SH in bash*|mksh) exit ;; esac
269
270unquoted='foo'
271sq='foo bar'
272bash1=$'\x1f' # ASCII control char
273bash2=$'\xfe\xff' # Invalid UTF-8
274
275s1=$unquoted
276s2=$sq
277s3=$bash1
278s4=$bash2
279
280declare -a a=("$unquoted" "$sq" "$bash1" "$bash2")
281declare -A A=(["$unquoted"]="$sq" ["$bash1"]="$bash2")
282
283#echo lengths ${#s1} ${#s2} ${#s3} ${#s4} ${#a[@]} ${#A[@]}
284
285declare -p s1 s2 s3 s4 a A | tee tmp.bash
286
287echo ---
288
289bash -c 'source tmp.bash; echo "$s1 $s2"; echo -n "$s3" "$s4" | od -A n -t x1'
290echo bash=$?
291
292## STDOUT:
293declare -- s1=foo
294declare -- s2='foo bar'
295declare -- s3=$'\u001f'
296declare -- s4=$'\xfe\xff'
297declare -a a=(foo 'foo bar' $'\u001f' $'\xfe\xff')
298declare -A A=([$'\u001f']=$'\xfe\xff' ['foo']='foo bar')
299---
300foo foo bar
301 1f 20 fe ff
302bash=0
303## END
304
305## N-I bash/mksh STDOUT:
306## END
307
308
309
310#### declare -p var
311# BUG? bash doesn't output anything for 'local/readonly -p var', which seems to
312# contradict with manual. Besides, 'export -p var' is not described in
313# manual
314test_var1=111
315readonly test_var2=222
316export test_var3=333
317declare -n test_var4=test_var1
318f1() {
319 local test_var5=555
320 {
321 echo '[declare]'
322 declare -p test_var{0..5}
323 echo '[readonly]'
324 readonly -p test_var{0..5}
325 echo '[export]'
326 export -p test_var{0..5}
327 echo '[local]'
328 local -p test_var{0..5}
329 } | grep -E '^\[|^\b.*test_var.\b'
330}
331f1
332## STDOUT:
333[declare]
334declare -- test_var1=111
335declare -r test_var2=222
336declare -x test_var3=333
337declare -n test_var4=test_var1
338declare -- test_var5=555
339[readonly]
340declare -r test_var2=222
341[export]
342declare -x test_var3=333
343[local]
344declare -- test_var5=555
345## END
346## BUG bash STDOUT:
347[declare]
348declare -- test_var1="111"
349declare -r test_var2="222"
350declare -x test_var3="333"
351declare -n test_var4="test_var1"
352declare -- test_var5="555"
353[readonly]
354[export]
355[local]
356## END
357## N-I mksh STDOUT:
358[declare]
359[readonly]
360## END
361
362#### declare -p arr
363test_arr1=()
364declare -a test_arr2=()
365declare -A test_arr3=()
366test_arr4=(1 2 3)
367declare -a test_arr5=(1 2 3)
368declare -A test_arr6=(['a']=1 ['b']=2 ['c']=3)
369test_arr7=()
370test_arr7[3]=foo
371declare -p test_arr{1..7}
372## STDOUT:
373declare -a test_arr1=()
374declare -a test_arr2=()
375declare -A test_arr3=()
376declare -a test_arr4=(1 2 3)
377declare -a test_arr5=(1 2 3)
378declare -A test_arr6=(['a']=1 ['b']=2 ['c']=3)
379declare -a test_arr7=(); test_arr7[3]=foo
380## END
381## OK bash STDOUT:
382declare -a test_arr1=()
383declare -a test_arr2=()
384declare -A test_arr3=()
385declare -a test_arr4=([0]="1" [1]="2" [2]="3")
386declare -a test_arr5=([0]="1" [1]="2" [2]="3")
387declare -A test_arr6=([a]="1" [b]="2" [c]="3" )
388declare -a test_arr7=([3]="foo")
389## END
390## N-I mksh stdout-json: ""
391## N-I mksh status: 1
392
393#### declare -p foo=bar doesn't make sense
394case $SH in (mksh) exit 0; esac
395
396declare -p foo=bar
397echo status=$?
398
399a=b
400declare -p a foo=bar > tmp.txt
401echo status=$?
402sed 's/"//g' tmp.txt # don't care about quotes
403## STDOUT:
404status=1
405status=1
406declare -- a=b
407## END
408## N-I mksh stdout-json: ""
409
410#### declare -pnrx
411test_var1=111
412readonly test_var2=222
413export test_var3=333
414declare -n test_var4=test_var1
415f1() {
416 local test_var5=555
417 {
418 echo '[declare -pn]'
419 declare -pn
420 echo '[declare -pr]'
421 declare -pr
422 echo '[declare -px]'
423 declare -px
424 } | grep -E '^\[|^\b.*test_var.\b'
425}
426f1
427## STDOUT:
428[declare -pn]
429declare -n test_var4=test_var1
430[declare -pr]
431declare -r test_var2=222
432[declare -px]
433declare -x test_var3=333
434## END
435## OK bash STDOUT:
436[declare -pn]
437declare -n test_var4="test_var1"
438[declare -pr]
439declare -r test_var2="222"
440[declare -px]
441declare -x test_var3="333"
442## END
443## N-I mksh STDOUT:
444[declare -pn]
445[declare -pr]
446[declare -px]
447## END
448
449#### declare -paA
450declare -a test_var6=()
451declare -A test_var7=()
452f1() {
453 {
454 echo '[declare -pa]'
455 declare -pa
456 echo '[declare -pA]'
457 declare -pA
458 } | grep -E '^\[|^\b.*test_var.\b'
459}
460f1
461## STDOUT:
462[declare -pa]
463declare -a test_var6=()
464[declare -pA]
465declare -A test_var7=()
466## END
467## OK bash STDOUT:
468[declare -pa]
469declare -a test_var6=()
470[declare -pA]
471declare -A test_var7=()
472## END
473## N-I mksh stdout-json: ""
474## N-I mksh status: 1
475
476#### declare -pnrx var
477# Note: Bash ignores other flags (-nrx) when variable names are supplied while
478# OSH uses other flags to select variables. Bash's behavior is documented.
479test_var1=111
480readonly test_var2=222
481export test_var3=333
482declare -n test_var4=test_var1
483f1() {
484 local test_var5=555
485 {
486 echo '[declare -pn]'
487 declare -pn test_var{0..5}
488 echo '[declare -pr]'
489 declare -pr test_var{0..5}
490 echo '[declare -px]'
491 declare -px test_var{0..5}
492 } | grep -E '^\[|^\b.*test_var.\b'
493}
494f1
495## STDOUT:
496[declare -pn]
497declare -n test_var4=test_var1
498[declare -pr]
499declare -r test_var2=222
500[declare -px]
501declare -x test_var3=333
502## END
503## N-I bash STDOUT:
504[declare -pn]
505declare -- test_var1="111"
506declare -r test_var2="222"
507declare -x test_var3="333"
508declare -n test_var4="test_var1"
509declare -- test_var5="555"
510[declare -pr]
511declare -- test_var1="111"
512declare -r test_var2="222"
513declare -x test_var3="333"
514declare -n test_var4="test_var1"
515declare -- test_var5="555"
516[declare -px]
517declare -- test_var1="111"
518declare -r test_var2="222"
519declare -x test_var3="333"
520declare -n test_var4="test_var1"
521declare -- test_var5="555"
522## END
523## N-I mksh STDOUT:
524[declare -pn]
525[declare -pr]
526[declare -px]
527## END
528
529#### declare -pg
530test_var1=global
531f1() {
532 local test_var1=local
533 {
534 declare -pg
535 } | grep -E '^\[|^\b[^"]*test_var.\b'
536}
537f1
538## STDOUT:
539declare -- test_var1=global
540## END
541## N-I bash STDOUT:
542declare -- test_var1="local"
543## END
544## N-I mksh stdout-json: ""
545## N-I mksh status: 1
546
547#### declare -pg var
548test_var1=global
549f1() {
550 local test_var1=local
551 {
552 declare -pg test_var1
553 } | grep -E '^\[|^\b.*test_var.\b'
554}
555f1
556## STDOUT:
557declare -- test_var1=global
558## END
559## N-I bash STDOUT:
560declare -- test_var1="local"
561## END
562## N-I mksh stdout-json: ""
563## N-I mksh status: 1
564
565#### ble.sh: eval -- "$(declare -p var arr)"
566# This illustrates an example usage of "eval & declare" for exporting
567# multiple variables from $().
568eval -- "$(
569 printf '%s\n' a{1..10} | {
570 sum=0 i=0 arr=()
571 while read line; do
572 ((sum+=${#line},i++))
573 arr[$((i/3))]=$line
574 done
575 declare -p sum arr
576 })"
577echo sum=$sum
578for ((i=0;i<${#arr[@]};i++)); do
579 echo "arr[$i]=${arr[i]}"
580done
581## STDOUT:
582sum=21
583arr[0]=a2
584arr[1]=a5
585arr[2]=a8
586arr[3]=a10
587## END
588## N-I mksh stdout-json: ""
589## N-I mksh status: 1
590
591#### eval -- "$(declare -p arr)" (restore arrays w/ unset elements)
592arr=(1 2 3)
593eval -- "$(arr=(); arr[3]= arr[4]=foo; declare -p arr)"
594for i in {0..4}; do
595 echo "arr[$i]: ${arr[$i]+set ... [}${arr[$i]-unset}${arr[$i]+]}"
596done
597## STDOUT:
598arr[0]: unset
599arr[1]: unset
600arr[2]: unset
601arr[3]: set ... []
602arr[4]: set ... [foo]
603## END
604## N-I mksh stdout-json: ""
605## N-I mksh status: 1
606
607#### typeset -f
608# mksh implement typeset but not declare
609typeset -f myfunc func2
610echo $?
611
612myfunc() { echo myfunc; }
613# This prints the source code.
614typeset -f myfunc func2 > /dev/null
615echo $?
616
617func2() { echo func2; }
618typeset -f myfunc func2 > /dev/null
619echo $?
620## STDOUT:
6211
6221
6230
624## END
625
626#### typeset -p
627var1() { echo func; } # function names are NOT found.
628typeset -p var1 var2 >/dev/null
629echo $?
630
631var1=x
632typeset -p var1 var2 >/dev/null
633echo $?
634
635var2=y
636typeset -p var1 var2 >/dev/null
637echo $?
638## STDOUT:
6391
6401
6410
642## BUG mksh STDOUT:
643# mksh doesn't respect exit codes
6440
6450
6460
647## END
648
649#### typeset -r makes a string readonly
650typeset -r s1='12'
651typeset -r s2='34'
652
653s1='c'
654echo status=$?
655s2='d'
656echo status=$?
657
658s1+='e'
659echo status=$?
660s2+='f'
661echo status=$?
662
663unset s1
664echo status=$?
665unset s2
666echo status=$?
667
668## status: 1
669## stdout-json: ""
670## OK mksh status: 2
671## OK bash status: 0
672## OK bash STDOUT:
673status=1
674status=1
675status=1
676status=1
677status=1
678status=1
679## END
680
681#### typeset -ar makes it readonly
682typeset -a -r array1=(1 2)
683typeset -ar array2=(3 4)
684
685array1=('c')
686echo status=$?
687array2=('d')
688echo status=$?
689
690array1+=('e')
691echo status=$?
692array2+=('f')
693echo status=$?
694
695unset array1
696echo status=$?
697unset array2
698echo status=$?
699
700## status: 1
701## stdout-json: ""
702## OK bash status: 0
703## OK bash STDOUT:
704status=1
705status=1
706status=1
707status=1
708status=1
709status=1
710## END
711## N-I mksh status: 1
712## N-I mksh stdout-json: ""
713
714#### typeset -x makes it exported
715typeset -rx PYTHONPATH=lib/
716printenv.py PYTHONPATH
717## STDOUT:
718lib/
719## END
720
721#### Multiple assignments / array assignments on a line
722a=1 b[0+0]=2 c=3
723echo $a ${b[@]} $c
724## stdout: 1 2 3
725
726#### Env bindings shouldn't contain array assignments
727a=1 b[0]=2 c=3 printenv.py a b c
728## status: 2
729## stdout-json: ""
730## OK bash STDOUT:
7311
732None
7333
734## END
735## OK bash status: 0
736## BUG mksh STDOUT:
7371
7382
7393
740## END
741## BUG mksh status: 0
742
743#### syntax error in array assignment
744a=x b[0+]=y c=z
745echo $a $b $c
746## status: 2
747## stdout-json: ""
748## BUG bash stdout: x
749## BUG bash status: 0
750## OK mksh stdout-json: ""
751## OK mksh status: 1
752
753#### declare -g (bash-specific; bash-completion uses it)
754f() {
755 declare -g G=42
756 declare L=99
757
758 declare -Ag dict
759 dict["foo"]=bar
760
761 declare -A localdict
762 localdict["spam"]=Eggs
763
764 # For bash-completion
765 eval 'declare -Ag ev'
766 ev["ev1"]=ev2
767}
768f
769argv.py "$G" "$L"
770argv.py "${dict["foo"]}" "${localdict["spam"]}"
771argv.py "${ev["ev1"]}"
772## STDOUT:
773['42', '']
774['bar', '']
775['ev2']
776## END
777## N-I mksh STDOUT:
778['', '']
779## END
780## N-I mksh status: 1
781
782#### myvar=typeset (another form of dynamic assignment)
783myvar=typeset
784x='a b'
785$myvar x=$x
786echo $x
787## STDOUT:
788a
789## END
790## OK osh STDOUT:
791a b
792## END
793
794#### dynamic array parsing is not allowed
795code='x=(1 2 3)'
796typeset -a "$code" # note: -a flag is required
797echo status=$?
798argv.py "$x"
799## STDOUT:
800status=2
801['']
802## END
803## OK mksh STDOUT:
804status=0
805['(1 2 3)']
806## END
807# bash allows it
808## OK bash STDOUT:
809status=0
810['1']
811## END
812
813#### dynamic flag in array in assign builtin
814typeset b
815b=(unused1 unused2) # this works in mksh
816
817a=(x 'foo=F' 'bar=B')
818typeset -"${a[@]}"
819echo foo=$foo
820echo bar=$bar
821printenv.py foo
822printenv.py bar
823
824# syntax error in mksh! But works in bash and zsh.
825#typeset -"${a[@]}" b=(spam eggs)
826#echo "length of b = ${#b[@]}"
827#echo "b[0]=${b[0]}"
828#echo "b[1]=${b[1]}"
829
830## STDOUT:
831foo=F
832bar=B
833F
834B
835## END
836
837#### typeset +x
838export e=E
839printenv.py e
840typeset +x e=E2
841printenv.py e # no longer exported
842## STDOUT:
843E
844None
845## END
846
847#### typeset +r removes read-only attribute (TODO: documented in bash to do nothing)
848readonly r=r1
849echo r=$r
850
851# clear the readonly flag. Why is this accepted in bash, but doesn't do
852# anything?
853typeset +r r=r2
854echo r=$r
855
856r=r3
857echo r=$r
858
859## status: 0
860## STDOUT:
861r=r1
862r=r2
863r=r3
864## END
865
866# mksh doesn't allow you to unset
867## OK mksh status: 2
868## OK mksh STDOUT:
869r=r1
870## END
871
872# bash doesn't allow you to unset
873## OK bash status: 0
874## OK bash STDOUT:
875r=r1
876r=r1
877r=r1
878## END
879
880
881#### function name with /
882ble/foo() { echo hi; }
883declare -F ble/foo
884echo status=$?
885## STDOUT:
886ble/foo
887status=0
888## END
889## N-I mksh stdout: status=127
890## N-I zsh stdout-json: ""
891## N-I zsh status: 1
892## N-I ash stdout-json: ""
893## N-I ash status: 2
894
895#### invalid var name
896typeset foo/bar
897## status: 1
898
899#### unset and shell funcs
900foo() {
901 echo bar
902}
903
904foo
905
906declare -F
907unset foo
908declare -F
909
910foo
911
912## status: 127
913## STDOUT:
914bar
915declare -f foo
916## END
917## N-I mksh status: 0
918## N-I mksh STDOUT:
919bar
920bar
921## END