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)
8 f() {
9 local -a array=(x y z)
10 argv.py "${array[@]}"
11 }
12 f
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)
19 declare -a array=(x y z)
20 argv.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)
26 a[1 * 1]=x a[ 1 + 2 ]=z
27 echo status=$?
28 argv.py "${a[@]}"
29 ## STDOUT:
30 status=0
31 ['x', 'z']
32 ## END
33 ## N-I osh STDOUT:
34 status=127
35 []
36 ## END
37
38 #### declare -f exit code indicates function existence
39 func2=x # var names are NOT found
40 declare -f myfunc func2
41 echo $?
42
43 myfunc() { echo myfunc; }
44 # This prints the source code.
45 declare -f myfunc func2 > /dev/null
46 echo $?
47
48 func2() { echo func2; }
49 declare -f myfunc func2 > /dev/null
50 echo $?
51 ## STDOUT:
52 1
53 1
54 0
55 ## END
56 ## N-I mksh STDOUT:
57 127
58 127
59 127
60 ## END
61
62 #### declare -F prints function names
63 add () { expr 4 + 4; }
64 div () { expr 6 / 2; }
65 ek () { echo hello; }
66 __ec () { echo hi; }
67 _ab () { expr 10 % 3; }
68
69 declare -F
70 ## STDOUT:
71 declare -f __ec
72 declare -f _ab
73 declare -f add
74 declare -f div
75 declare -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
81 case $SH in mksh) exit ;; esac
82
83 source $REPO_ROOT/spec/testdata/bash-source-2.sh
84
85 shopt -s extdebug
86
87 add () { expr 4 + 4; }
88
89 declare -F
90 echo
91
92 declare -F add
93 # in bash-source-2
94 declare -F g | sed "s;$REPO_ROOT;ROOT;g"
95
96 ## STDOUT:
97 declare -f add
98 declare -f g
99
100 add 7 main
101 g 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
107 case $SH in mksh) exit ;; esac
108
109 $SH $REPO_ROOT/spec/testdata/extdebug.sh | sed "s;$REPO_ROOT;ROOT;g"
110
111 ## STDOUT:
112 declare -f add
113 declare -f g
114
115 add 5 ROOT/spec/testdata/extdebug.sh
116 g 3 ROOT/spec/testdata/bash-source-2.sh
117 ## END
118 ## N-I mksh STDOUT:
119 ## END
120
121 #### declare -p var (exit status)
122 var1() { echo func; } # function names are NOT found.
123 declare -p var1 var2 >/dev/null
124 echo $?
125
126 var1=x
127 declare -p var1 var2 >/dev/null
128 echo $?
129
130 var2=y
131 declare -p var1 var2 >/dev/null
132 echo $?
133 ## STDOUT:
134 1
135 1
136 0
137 ## N-I mksh STDOUT:
138 127
139 127
140 127
141 ## END
142
143 #### declare
144 test_var1=111
145 readonly test_var2=222
146 export test_var3=333
147 declare -n test_var4=test_var1
148 f1() {
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 }
161 f1
162 ## STDOUT:
163 [declare]
164 test_var1=111
165 test_var2=222
166 test_var3=333
167 test_var4=test_var1
168 test_var5=555
169 [readonly]
170 declare -r test_var2=222
171 [export]
172 declare -x test_var3=333
173 [local]
174 test_var5=555
175 ## END
176 ## OK bash STDOUT:
177 [declare]
178 test_var1=111
179 test_var2=222
180 test_var3=333
181 test_var4=test_var1
182 test_var5=555
183 [readonly]
184 declare -r test_var2="222"
185 [export]
186 declare -x test_var3="333"
187 [local]
188 test_var5=555
189 ## END
190 ## N-I mksh STDOUT:
191 [declare]
192 [readonly]
193 test_var2
194 [export]
195 test_var3
196 [local]
197 typeset test_var1
198 typeset -r test_var2
199 typeset -x test_var3
200 typeset 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.
206 test_var1=111
207 readonly test_var2=222
208 export test_var3=333
209 declare -n test_var4=test_var1
210 f1() {
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 }
223 f1
224 ## STDOUT:
225 [declare]
226 declare -- test_var1=111
227 declare -r test_var2=222
228 declare -x test_var3=333
229 declare -n test_var4=test_var1
230 declare -- test_var5=555
231 [readonly]
232 declare -r test_var2=222
233 [export]
234 declare -x test_var3=333
235 [local]
236 declare -- test_var5=555
237 ## END
238 ## BUG bash STDOUT:
239 [declare]
240 declare -- test_var1="111"
241 declare -r test_var2="222"
242 declare -x test_var3="333"
243 declare -n test_var4="test_var1"
244 declare -- test_var5="555"
245 [readonly]
246 declare -r test_var2="222"
247 [export]
248 declare -x test_var3="333"
249 [local]
250 test_var5=555
251 ## END
252 ## N-I mksh STDOUT:
253 [declare]
254 [readonly]
255 readonly test_var2=222
256 [export]
257 export test_var3=333
258 [local]
259 typeset test_var1=111
260 typeset -r test_var2=222
261 typeset -x test_var3=333
262 typeset 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!
268 case $SH in bash*|mksh) exit ;; esac
269
270 unquoted='foo'
271 sq='foo bar'
272 bash1=$'\x1f' # ASCII control char
273 bash2=$'\xfe\xff' # Invalid UTF-8
274
275 s1=$unquoted
276 s2=$sq
277 s3=$bash1
278 s4=$bash2
279
280 declare -a a=("$unquoted" "$sq" "$bash1" "$bash2")
281 declare -A A=(["$unquoted"]="$sq" ["$bash1"]="$bash2")
282
283 #echo lengths ${#s1} ${#s2} ${#s3} ${#s4} ${#a[@]} ${#A[@]}
284
285 declare -p s1 s2 s3 s4 a A | tee tmp.bash
286
287 echo ---
288
289 bash -c 'source tmp.bash; echo "$s1 $s2"; echo -n "$s3" "$s4" | od -A n -t x1'
290 echo bash=$?
291
292 ## STDOUT:
293 declare -- s1=foo
294 declare -- s2='foo bar'
295 declare -- s3=$'\u001f'
296 declare -- s4=$'\xfe\xff'
297 declare -a a=(foo 'foo bar' $'\u001f' $'\xfe\xff')
298 declare -A A=([$'\u001f']=$'\xfe\xff' ['foo']='foo bar')
299 ---
300 foo foo bar
301 1f 20 fe ff
302 bash=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
314 test_var1=111
315 readonly test_var2=222
316 export test_var3=333
317 declare -n test_var4=test_var1
318 f1() {
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 }
331 f1
332 ## STDOUT:
333 [declare]
334 declare -- test_var1=111
335 declare -r test_var2=222
336 declare -x test_var3=333
337 declare -n test_var4=test_var1
338 declare -- test_var5=555
339 [readonly]
340 declare -r test_var2=222
341 [export]
342 declare -x test_var3=333
343 [local]
344 declare -- test_var5=555
345 ## END
346 ## BUG bash STDOUT:
347 [declare]
348 declare -- test_var1="111"
349 declare -r test_var2="222"
350 declare -x test_var3="333"
351 declare -n test_var4="test_var1"
352 declare -- 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
363 test_arr1=()
364 declare -a test_arr2=()
365 declare -A test_arr3=()
366 test_arr4=(1 2 3)
367 declare -a test_arr5=(1 2 3)
368 declare -A test_arr6=(['a']=1 ['b']=2 ['c']=3)
369 test_arr7=()
370 test_arr7[3]=foo
371 declare -p test_arr{1..7}
372 ## STDOUT:
373 declare -a test_arr1=()
374 declare -a test_arr2=()
375 declare -A test_arr3=()
376 declare -a test_arr4=(1 2 3)
377 declare -a test_arr5=(1 2 3)
378 declare -A test_arr6=(['a']=1 ['b']=2 ['c']=3)
379 declare -a test_arr7=(); test_arr7[3]=foo
380 ## END
381 ## OK bash STDOUT:
382 declare -a test_arr1=()
383 declare -a test_arr2=()
384 declare -A test_arr3=()
385 declare -a test_arr4=([0]="1" [1]="2" [2]="3")
386 declare -a test_arr5=([0]="1" [1]="2" [2]="3")
387 declare -A test_arr6=([a]="1" [b]="2" [c]="3" )
388 declare -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
394 case $SH in (mksh) exit 0; esac
395
396 declare -p foo=bar
397 echo status=$?
398
399 a=b
400 declare -p a foo=bar > tmp.txt
401 echo status=$?
402 sed 's/"//g' tmp.txt # don't care about quotes
403 ## STDOUT:
404 status=1
405 status=1
406 declare -- a=b
407 ## END
408 ## N-I mksh stdout-json: ""
409
410 #### declare -pnrx
411 test_var1=111
412 readonly test_var2=222
413 export test_var3=333
414 declare -n test_var4=test_var1
415 f1() {
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 }
426 f1
427 ## STDOUT:
428 [declare -pn]
429 declare -n test_var4=test_var1
430 [declare -pr]
431 declare -r test_var2=222
432 [declare -px]
433 declare -x test_var3=333
434 ## END
435 ## OK bash STDOUT:
436 [declare -pn]
437 declare -n test_var4="test_var1"
438 [declare -pr]
439 declare -r test_var2="222"
440 [declare -px]
441 declare -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
450 declare -a test_var6=()
451 declare -A test_var7=()
452 f1() {
453 {
454 echo '[declare -pa]'
455 declare -pa
456 echo '[declare -pA]'
457 declare -pA
458 } | grep -E '^\[|^\b.*test_var.\b'
459 }
460 f1
461 ## STDOUT:
462 [declare -pa]
463 declare -a test_var6=()
464 [declare -pA]
465 declare -A test_var7=()
466 ## END
467 ## OK bash STDOUT:
468 [declare -pa]
469 declare -a test_var6=()
470 [declare -pA]
471 declare -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.
479 test_var1=111
480 readonly test_var2=222
481 export test_var3=333
482 declare -n test_var4=test_var1
483 f1() {
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 }
494 f1
495 ## STDOUT:
496 [declare -pn]
497 declare -n test_var4=test_var1
498 [declare -pr]
499 declare -r test_var2=222
500 [declare -px]
501 declare -x test_var3=333
502 ## END
503 ## N-I bash STDOUT:
504 [declare -pn]
505 declare -- test_var1="111"
506 declare -r test_var2="222"
507 declare -x test_var3="333"
508 declare -n test_var4="test_var1"
509 declare -- test_var5="555"
510 [declare -pr]
511 declare -- test_var1="111"
512 declare -r test_var2="222"
513 declare -x test_var3="333"
514 declare -n test_var4="test_var1"
515 declare -- test_var5="555"
516 [declare -px]
517 declare -- test_var1="111"
518 declare -r test_var2="222"
519 declare -x test_var3="333"
520 declare -n test_var4="test_var1"
521 declare -- 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
530 test_var1=global
531 f1() {
532 local test_var1=local
533 {
534 declare -pg
535 } | grep -E '^\[|^\b[^"]*test_var.\b'
536 }
537 f1
538 ## STDOUT:
539 declare -- test_var1=global
540 ## END
541 ## N-I bash STDOUT:
542 declare -- test_var1="local"
543 ## END
544 ## N-I mksh stdout-json: ""
545 ## N-I mksh status: 1
546
547 #### declare -pg var
548 test_var1=global
549 f1() {
550 local test_var1=local
551 {
552 declare -pg test_var1
553 } | grep -E '^\[|^\b.*test_var.\b'
554 }
555 f1
556 ## STDOUT:
557 declare -- test_var1=global
558 ## END
559 ## N-I bash STDOUT:
560 declare -- 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 $().
568 eval -- "$(
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 })"
577 echo sum=$sum
578 for ((i=0;i<${#arr[@]};i++)); do
579 echo "arr[$i]=${arr[i]}"
580 done
581 ## STDOUT:
582 sum=21
583 arr[0]=a2
584 arr[1]=a5
585 arr[2]=a8
586 arr[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)
592 arr=(1 2 3)
593 eval -- "$(arr=(); arr[3]= arr[4]=foo; declare -p arr)"
594 for i in {0..4}; do
595 echo "arr[$i]: ${arr[$i]+set ... [}${arr[$i]-unset}${arr[$i]+]}"
596 done
597 ## STDOUT:
598 arr[0]: unset
599 arr[1]: unset
600 arr[2]: unset
601 arr[3]: set ... []
602 arr[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
609 typeset -f myfunc func2
610 echo $?
611
612 myfunc() { echo myfunc; }
613 # This prints the source code.
614 typeset -f myfunc func2 > /dev/null
615 echo $?
616
617 func2() { echo func2; }
618 typeset -f myfunc func2 > /dev/null
619 echo $?
620 ## STDOUT:
621 1
622 1
623 0
624 ## END
625
626 #### typeset -p
627 var1() { echo func; } # function names are NOT found.
628 typeset -p var1 var2 >/dev/null
629 echo $?
630
631 var1=x
632 typeset -p var1 var2 >/dev/null
633 echo $?
634
635 var2=y
636 typeset -p var1 var2 >/dev/null
637 echo $?
638 ## STDOUT:
639 1
640 1
641 0
642 ## BUG mksh STDOUT:
643 # mksh doesn't respect exit codes
644 0
645 0
646 0
647 ## END
648
649 #### typeset -r makes a string readonly
650 typeset -r s1='12'
651 typeset -r s2='34'
652
653 s1='c'
654 echo status=$?
655 s2='d'
656 echo status=$?
657
658 s1+='e'
659 echo status=$?
660 s2+='f'
661 echo status=$?
662
663 unset s1
664 echo status=$?
665 unset s2
666 echo status=$?
667
668 ## status: 1
669 ## stdout-json: ""
670 ## OK mksh status: 2
671 ## OK bash status: 0
672 ## OK bash STDOUT:
673 status=1
674 status=1
675 status=1
676 status=1
677 status=1
678 status=1
679 ## END
680
681 #### typeset -ar makes it readonly
682 typeset -a -r array1=(1 2)
683 typeset -ar array2=(3 4)
684
685 array1=('c')
686 echo status=$?
687 array2=('d')
688 echo status=$?
689
690 array1+=('e')
691 echo status=$?
692 array2+=('f')
693 echo status=$?
694
695 unset array1
696 echo status=$?
697 unset array2
698 echo status=$?
699
700 ## status: 1
701 ## stdout-json: ""
702 ## OK bash status: 0
703 ## OK bash STDOUT:
704 status=1
705 status=1
706 status=1
707 status=1
708 status=1
709 status=1
710 ## END
711 ## N-I mksh status: 1
712 ## N-I mksh stdout-json: ""
713
714 #### typeset -x makes it exported
715 typeset -rx PYTHONPATH=lib/
716 printenv.py PYTHONPATH
717 ## STDOUT:
718 lib/
719 ## END
720
721 #### Multiple assignments / array assignments on a line
722 a=1 b[0+0]=2 c=3
723 echo $a ${b[@]} $c
724 ## stdout: 1 2 3
725
726 #### Env bindings shouldn't contain array assignments
727 a=1 b[0]=2 c=3 printenv.py a b c
728 ## status: 2
729 ## stdout-json: ""
730 ## OK bash STDOUT:
731 1
732 None
733 3
734 ## END
735 ## OK bash status: 0
736 ## BUG mksh STDOUT:
737 1
738 2
739 3
740 ## END
741 ## BUG mksh status: 0
742
743 #### syntax error in array assignment
744 a=x b[0+]=y c=z
745 echo $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)
754 f() {
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 }
768 f
769 argv.py "$G" "$L"
770 argv.py "${dict["foo"]}" "${localdict["spam"]}"
771 argv.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)
783 myvar=typeset
784 x='a b'
785 $myvar x=$x
786 echo $x
787 ## STDOUT:
788 a
789 ## END
790 ## OK osh STDOUT:
791 a b
792 ## END
793
794 #### dynamic array parsing is not allowed
795 code='x=(1 2 3)'
796 typeset -a "$code" # note: -a flag is required
797 echo status=$?
798 argv.py "$x"
799 ## STDOUT:
800 status=2
801 ['']
802 ## END
803 ## OK mksh STDOUT:
804 status=0
805 ['(1 2 3)']
806 ## END
807 # bash allows it
808 ## OK bash STDOUT:
809 status=0
810 ['1']
811 ## END
812
813 #### dynamic flag in array in assign builtin
814 typeset b
815 b=(unused1 unused2) # this works in mksh
816
817 a=(x 'foo=F' 'bar=B')
818 typeset -"${a[@]}"
819 echo foo=$foo
820 echo bar=$bar
821 printenv.py foo
822 printenv.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:
831 foo=F
832 bar=B
833 F
834 B
835 ## END
836
837 #### typeset +x
838 export e=E
839 printenv.py e
840 typeset +x e=E2
841 printenv.py e # no longer exported
842 ## STDOUT:
843 E
844 None
845 ## END
846
847 #### typeset +r removes read-only attribute (TODO: documented in bash to do nothing)
848 readonly r=r1
849 echo r=$r
850
851 # clear the readonly flag. Why is this accepted in bash, but doesn't do
852 # anything?
853 typeset +r r=r2
854 echo r=$r
855
856 r=r3
857 echo r=$r
858
859 ## status: 0
860 ## STDOUT:
861 r=r1
862 r=r2
863 r=r3
864 ## END
865
866 # mksh doesn't allow you to unset
867 ## OK mksh status: 2
868 ## OK mksh STDOUT:
869 r=r1
870 ## END
871
872 # bash doesn't allow you to unset
873 ## OK bash status: 0
874 ## OK bash STDOUT:
875 r=r1
876 r=r1
877 r=r1
878 ## END
879
880
881 #### function name with /
882 ble/foo() { echo hi; }
883 declare -F ble/foo
884 echo status=$?
885 ## STDOUT:
886 ble/foo
887 status=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
896 typeset foo/bar
897 ## status: 1
898
899 #### unset and shell funcs
900 foo() {
901 echo bar
902 }
903
904 foo
905
906 declare -F
907 unset foo
908 declare -F
909
910 foo
911
912 ## status: 127
913 ## STDOUT:
914 bar
915 declare -f foo
916 ## END
917 ## N-I mksh status: 0
918 ## N-I mksh STDOUT:
919 bar
920 bar
921 ## END