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