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