1 #
2 # Tests for builtins having to do with variables: export, readonly, unset, etc.
3 #
4 # Also see assign.test.sh.
5
6 #### Export sets a global variable
7 # Even after you do export -n, it still exists.
8 f() { export GLOBAL=X; }
9 f
10 echo $GLOBAL
11 printenv.py GLOBAL
12 ## STDOUT:
13 X
14 X
15 ## END
16
17 #### Export sets a global variable that persists after export -n
18 f() { export GLOBAL=X; }
19 f
20 echo $GLOBAL
21 printenv.py GLOBAL
22 export -n GLOBAL
23 echo $GLOBAL
24 printenv.py GLOBAL
25 ## STDOUT:
26 X
27 X
28 X
29 None
30 ## END
31 ## N-I mksh/dash STDOUT:
32 X
33 X
34 ## END
35 ## N-I mksh status: 1
36 ## N-I dash status: 2
37 ## N-I zsh STDOUT:
38 X
39 X
40 X
41 X
42 ## END
43
44 #### export -n undefined is ignored
45 set -o errexit
46 export -n undef
47 echo status=$?
48 ## stdout: status=0
49 ## N-I mksh/dash/zsh stdout-json: ""
50 ## N-I mksh status: 1
51 ## N-I dash status: 2
52 ## N-I zsh status: 1
53
54 #### export -n foo=bar not allowed
55 foo=old
56 export -n foo=new
57 echo status=$?
58 echo $foo
59 ## STDOUT:
60 status=2
61 old
62 ## END
63 ## OK bash STDOUT:
64 status=0
65 new
66 ## END
67 ## N-I zsh STDOUT:
68 status=1
69 old
70 ## END
71 ## N-I dash status: 2
72 ## N-I dash stdout-json: ""
73 ## N-I mksh status: 1
74 ## N-I mksh stdout-json: ""
75
76 #### Export a global variable and unset it
77 f() { export GLOBAL=X; }
78 f
79 echo $GLOBAL
80 printenv.py GLOBAL
81 unset GLOBAL
82 echo g=$GLOBAL
83 printenv.py GLOBAL
84 ## STDOUT:
85 X
86 X
87 g=
88 None
89 ## END
90
91 #### Export existing global variables
92 G1=g1
93 G2=g2
94 export G1 G2
95 printenv.py G1 G2
96 ## STDOUT:
97 g1
98 g2
99 ## END
100
101 #### Export existing local variable
102 f() {
103 local L1=local1
104 export L1
105 printenv.py L1
106 }
107 f
108 printenv.py L1
109 ## STDOUT:
110 local1
111 None
112 ## END
113
114 #### Export a local that shadows a global
115 V=global
116 f() {
117 local V=local1
118 export V
119 printenv.py V
120 }
121 f
122 printenv.py V # exported local out of scope; global isn't exported yet
123 export V
124 printenv.py V # now it's exported
125 ## STDOUT:
126 local1
127 None
128 global
129 ## END
130
131 #### Export a variable before defining it
132 export U
133 U=u
134 printenv.py U
135 ## stdout: u
136
137 #### Unset exported variable, then define it again. It's NOT still exported.
138 export U
139 U=u
140 printenv.py U
141 unset U
142 printenv.py U
143 U=newvalue
144 echo $U
145 printenv.py U
146 ## STDOUT:
147 u
148 None
149 newvalue
150 None
151 ## END
152
153 #### Exporting a parent func variable (dynamic scope)
154 # The algorithm is to walk up the stack and export that one.
155 inner() {
156 export outer_var
157 echo "inner: $outer_var"
158 printenv.py outer_var
159 }
160 outer() {
161 local outer_var=X
162 echo "before inner"
163 printenv.py outer_var
164 inner
165 echo "after inner"
166 printenv.py outer_var
167 }
168 outer
169 ## STDOUT:
170 before inner
171 None
172 inner: X
173 X
174 after inner
175 X
176 ## END
177
178 #### Dependent export setting
179 # FOO is not respected here either.
180 export FOO=foo v=$(printenv.py FOO)
181 echo "v=$v"
182 ## stdout: v=None
183
184 #### Exporting a variable doesn't change it
185 old=$PATH
186 export PATH
187 new=$PATH
188 test "$old" = "$new" && echo "not changed"
189 ## stdout: not changed
190
191 #### can't export array
192 typeset -a a
193 a=(1 2 3)
194 export a
195 printenv.py a
196 ## STDOUT:
197 None
198 ## END
199 ## BUG mksh STDOUT:
200 1
201 ## END
202 ## N-I dash status: 2
203 ## N-I dash stdout-json: ""
204 ## OK osh status: 1
205 ## OK osh stdout-json: ""
206
207 #### can't export associative array
208 typeset -A a
209 a["foo"]=bar
210 export a
211 printenv.py a
212 ## STDOUT:
213 None
214 ## END
215 ## N-I mksh status: 1
216 ## N-I mksh stdout-json: ""
217 ## OK osh status: 1
218 ## OK osh stdout-json: ""
219
220 #### assign to readonly variable
221 # bash doesn't abort unless errexit!
222 readonly foo=bar
223 foo=eggs
224 echo "status=$?" # nothing happens
225 ## status: 1
226 ## BUG bash stdout: status=1
227 ## BUG bash status: 0
228 ## OK dash/mksh status: 2
229
230 #### Make an existing local variable readonly
231 f() {
232 local x=local
233 readonly x
234 echo $x
235 eval 'x=bar' # Wrap in eval so it's not fatal
236 echo status=$?
237 }
238 x=global
239 f
240 echo $x
241 ## STDOUT:
242 local
243 status=1
244 global
245 ## END
246 ## OK dash STDOUT:
247 local
248 ## END
249 ## OK dash status: 2
250
251 # mksh aborts the function, weird
252 ## OK mksh STDOUT:
253 local
254 global
255 ## END
256
257 #### assign to readonly variable - errexit
258 set -o errexit
259 readonly foo=bar
260 foo=eggs
261 echo "status=$?" # nothing happens
262 ## status: 1
263 ## OK dash/mksh status: 2
264
265 #### Unset a variable
266 foo=bar
267 echo foo=$foo
268 unset foo
269 echo foo=$foo
270 ## STDOUT:
271 foo=bar
272 foo=
273 ## END
274
275 #### Unset exit status
276 V=123
277 unset V
278 echo status=$?
279 ## stdout: status=0
280
281 #### Unset nonexistent variable
282 unset ZZZ
283 echo status=$?
284 ## stdout: status=0
285
286 #### Unset readonly variable
287 # dash and zsh abort the whole program. OSH doesn't?
288 readonly R=foo
289 unset R
290 echo status=$?
291 ## status: 0
292 ## stdout: status=1
293 ## OK dash status: 2
294 ## OK dash stdout-json: ""
295 ## OK zsh status: 1
296 ## OK zsh stdout-json: ""
297
298 #### Unset a function without -f
299 f() {
300 echo foo
301 }
302 f
303 unset f
304 f
305 ## stdout: foo
306 ## status: 127
307 ## N-I dash/mksh/zsh status: 0
308 ## N-I dash/mksh/zsh STDOUT:
309 foo
310 foo
311 ## END
312
313 #### Unset has dynamic scope
314 f() {
315 unset foo
316 }
317 foo=bar
318 echo foo=$foo
319 f
320 echo foo=$foo
321 ## STDOUT:
322 foo=bar
323 foo=
324 ## END
325
326 #### Unset and scope (bug #653)
327 unlocal() { unset "$@"; }
328
329 level2() {
330 local hello=yy
331
332 echo level2=$hello
333 unlocal hello
334 echo level2=$hello
335 }
336
337 level1() {
338 local hello=xx
339
340 level2
341
342 echo level1=$hello
343 unlocal hello
344 echo level1=$hello
345
346 level2
347 }
348
349 hello=global
350 level1
351
352 # bash, mksh, yash agree here.
353 ## STDOUT:
354 level2=yy
355 level2=xx
356 level1=xx
357 level1=global
358 level2=yy
359 level2=global
360 ## END
361 ## OK dash/ash/zsh STDOUT:
362 level2=yy
363 level2=
364 level1=xx
365 level1=
366 level2=yy
367 level2=
368 ## END
369
370 #### unset of local reveals variable in higher scope
371
372 # Oil has a RARE behavior here (matching yash and mksh), but at least it's
373 # consistent.
374
375 x=global
376 f() {
377 local x=foo
378 echo x=$x
379 unset x
380 echo x=$x
381 }
382 f
383 ## STDOUT:
384 x=foo
385 x=global
386 ## END
387 ## OK dash/bash/zsh/ash STDOUT:
388 x=foo
389 x=
390 ## END
391
392 #### Unset invalid variable name
393 unset %
394 echo status=$?
395 ## STDOUT:
396 status=2
397 ## END
398 ## OK bash/mksh STDOUT:
399 status=1
400 ## END
401 ## BUG zsh STDOUT:
402 status=0
403 ## END
404 # dash does a hard failure!
405 ## OK dash stdout-json: ""
406 ## OK dash status: 2
407
408 #### Unset nonexistent variable
409 unset _nonexistent__
410 echo status=$?
411 ## STDOUT:
412 status=0
413 ## END
414
415 #### Unset -v
416 foo() {
417 echo "function foo"
418 }
419 foo=bar
420 unset -v foo
421 echo foo=$foo
422 foo
423 ## STDOUT:
424 foo=
425 function foo
426 ## END
427
428 #### Unset -f
429 foo() {
430 echo "function foo"
431 }
432 foo=bar
433 unset -f foo
434 echo foo=$foo
435 foo
436 echo status=$?
437 ## STDOUT:
438 foo=bar
439 status=127
440 ## END
441
442 #### Unset array member
443 shopt -s eval_unsafe_arith
444
445 a=(x y z)
446 unset 'a[1]'
447 echo status=$?
448 echo "${a[@]}" len="${#a[@]}"
449 ## STDOUT:
450 status=0
451 x z len=2
452 ## END
453 ## N-I dash status: 2
454 ## N-I dash stdout-json: ""
455 ## OK zsh STDOUT:
456 status=0
457 y z len=3
458 ## END
459
460 #### Unset errors
461 shopt -s eval_unsafe_arith
462
463 unset undef
464 echo status=$?
465
466 a=(x y z)
467 unset 'a[99]' # out of range
468 echo status=$?
469
470 unset 'not_array[99]' # not an array
471 echo status=$?
472
473 ## STDOUT:
474 status=0
475 status=0
476 status=0
477 ## END
478 ## N-I dash status: 2
479 ## N-I dash STDOUT:
480 status=0
481 ## END
482
483 #### Unset wrong type
484 case $SH in (mksh) exit ;; esac
485
486 shopt -s eval_unsafe_arith || true
487
488 declare undef
489 unset -v 'undef[1]'
490 echo undef $?
491 unset -v 'undef["key"]'
492 echo undef $?
493
494 declare a=(one two)
495 unset -v 'a[1]'
496 echo array $?
497
498 #shopt -s strict_arith || true
499 # In Oil, the string 'key' is converted to an integer, which is 0, unless
500 # strict_arith is on, when it fails.
501 unset -v 'a["key"]'
502 echo array $?
503
504 declare -A A=(['key']=val)
505 unset -v 'A[1]'
506 echo assoc $?
507 unset -v 'A["key"]'
508 echo assoc $?
509
510 ## STDOUT:
511 undef 1
512 undef 1
513 array 0
514 array 1
515 assoc 0
516 assoc 0
517 ## END
518 ## OK osh STDOUT:
519 undef 1
520 undef 1
521 array 0
522 array 0
523 assoc 0
524 assoc 0
525 ## END
526 ## BUG zsh STDOUT:
527 undef 0
528 undef 1
529 array 0
530 array 1
531 assoc 0
532 assoc 0
533 ## END
534 ## N-I dash/mksh stdout-json: ""
535 ## N-I dash status: 2
536
537
538 #### unset -v assoc (related to issue #661)
539 shopt -s eval_unsafe_arith || true
540
541 case $SH in (dash|mksh|zsh) return; esac
542
543 declare -A dict=()
544 key=1],a[1
545 dict["$key"]=foo
546 echo ${#dict[@]}
547 echo keys=${!dict[@]}
548 echo vals=${dict[@]}
549
550 unset -v 'dict["$key"]'
551 echo ${#dict[@]}
552 echo keys=${!dict[@]}
553 echo vals=${dict[@]}
554 ## STDOUT:
555 1
556 keys=1],a[1
557 vals=foo
558 0
559 keys=
560 vals=
561 ## END
562 ## N-I dash/mksh/zsh stdout-json: ""
563
564 #### unset assoc errors
565 shopt -s eval_unsafe_arith || true
566
567 case $SH in (dash|mksh) return; esac
568
569 declare -A assoc=(['key']=value)
570 unset 'assoc["nonexistent"]'
571 echo status=$?
572
573 ## STDOUT:
574 status=0
575 ## END
576 ## N-I dash/mksh stdout-json: ""
577
578
579 #### Unset array member with dynamic parsing
580 shopt -s eval_unsafe_arith
581
582 i=1
583 a=(w x y z)
584 unset 'a[ i - 1 ]' a[i+1] # note: can't have space between a and [
585 echo status=$?
586 echo "${a[@]}" len="${#a[@]}"
587 ## STDOUT:
588 status=0
589 x z len=2
590 ## END
591 ## N-I dash status: 2
592 ## N-I dash stdout-json: ""
593 ## N-I zsh status: 1
594 ## N-I zsh stdout-json: ""
595
596 #### Use local twice
597 f() {
598 local foo=bar
599 local foo
600 echo $foo
601 }
602 f
603 ## stdout: bar
604 ## BUG zsh STDOUT:
605 foo=bar
606 bar
607 ## END
608
609 #### Local without variable is still unset!
610 set -o nounset
611 f() {
612 local foo
613 echo "[$foo]"
614 }
615 f
616 ## stdout-json: ""
617 ## status: 1
618 ## OK dash status: 2
619 # zsh doesn't support nounset?
620 ## BUG zsh stdout: []
621 ## BUG zsh status: 0
622
623 #### local after readonly
624 f() {
625 readonly y
626 local x=1 y=$(( x ))
627 echo y=$y
628 }
629 f
630 echo y=$y
631 ## status: 1
632 ## stdout-json: ""
633
634 ## OK dash status: 2
635
636 ## BUG mksh status: 0
637 ## BUG mksh STDOUT:
638 y=0
639 y=
640 ## END
641
642 ## BUG bash status: 0
643 ## BUG bash STDOUT:
644 y=
645 y=
646 ## END
647
648 #### unset a[-1] (bf.bash regression)
649 case $SH in (dash|zsh) exit ;; esac
650
651 shopt -s eval_unsafe_arith
652 a=(1 2 3)
653 unset a[-1]
654 echo len=${#a[@]}
655
656 echo last=${a[-1]}
657 (( last = a[-1] ))
658 echo last=$last
659
660 (( a[-1] = 42 ))
661 echo "${a[@]}"
662
663 ## STDOUT:
664 len=2
665 last=2
666 last=2
667 1 42
668 ## END
669 ## BUG mksh STDOUT:
670 len=3
671 last=
672 last=0
673 1 2 3 42
674 ## END
675 ## N-I dash/zsh stdout-json: ""
676
677
678 #### unset a[-1] in sparse array (bf.bash regression)
679 case $SH in (dash|zsh) exit ;; esac
680
681 shopt -s eval_unsafe_arith
682 a=(0 1 2 3 4)
683 unset a[1]
684 unset a[4]
685 echo len=${#a[@]} a=${a[@]}
686 echo last=${a[-1]} second=${a[-2]} third=${a[-3]}
687
688 echo ---
689 unset a[3]
690 echo len=${#a[@]} a=${a[@]}
691 echo last=${a[-1]} second=${a[-2]} third=${a[-3]}
692
693 ## STDOUT:
694 len=3 a=0 2 3
695 last=3 second=2 third=
696 ---
697 len=2 a=0 2
698 last=2 second= third=0
699 ## END
700
701 ## BUG mksh STDOUT:
702 len=3 a=0 2 3
703 last= second= third=
704 ---
705 len=2 a=0 2
706 last= second= third=
707 ## END
708
709 ## N-I dash/zsh stdout-json: ""