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