1 #!/usr/bin/env bash
2 #
3 # Tests for builtins having to do with variables: export, readonly, unset, etc.
4 #
5 # Also see assign.test.sh.
6
7 ### Export sets a global variable
8 # Even after you do export -n, it still exists.
9 f() { export GLOBAL=X; }
10 f
11 echo $GLOBAL
12 printenv.py GLOBAL
13 # stdout-json: "X\nX\n"
14
15 ### Export sets a global variable that persists after export -n
16 f() { export GLOBAL=X; }
17 f
18 echo $GLOBAL
19 printenv.py GLOBAL
20 export -n GLOBAL
21 echo $GLOBAL
22 printenv.py GLOBAL
23 # stdout-json: "X\nX\nX\nNone\n"
24 # N-I mksh/dash stdout-json: "X\nX\n"
25 # N-I mksh status: 1
26 # N-I dash status: 2
27
28 ### export -n undefined is ignored
29 set -o errexit
30 export -n undef
31 echo status=$?
32 # stdout: status=0
33 # N-I mksh/dash stdout-json: ""
34 # N-I mksh status: 1
35 # N-I dash status: 2
36
37 ### Export a global variable and unset it
38 f() { export GLOBAL=X; }
39 f
40 echo $GLOBAL
41 printenv.py GLOBAL
42 unset GLOBAL
43 echo $GLOBAL
44 printenv.py GLOBAL
45 # stdout-json: "X\nX\n\nNone\n"
46
47 ### Export existing global variables
48 G1=g1
49 G2=g2
50 export G1 G2
51 printenv.py G1 G2
52 # stdout-json: "g1\ng2\n"
53
54 ### Export existing local variable
55 f() {
56 local L1=local1
57 export L1
58 printenv.py L1
59 }
60 f
61 printenv.py L1
62 # stdout-json: "local1\nNone\n"
63
64 ### Export a local that shadows a global
65 V=global
66 f() {
67 local V=local1
68 export V
69 printenv.py V
70 }
71 f
72 printenv.py V # exported local out of scope; global isn't exported yet
73 export V
74 printenv.py V # now it's exported
75 # stdout-json: "local1\nNone\nglobal\n"
76
77 ### Export a variable before defining it
78 export U
79 U=u
80 printenv.py U
81 # stdout: u
82
83 ### Exporting a parent func variable (dynamic scope)
84 # The algorithm is to walk up the stack and export that one.
85 inner() {
86 export outer_var
87 echo "inner: $outer_var"
88 printenv.py outer_var
89 }
90 outer() {
91 local outer_var=X
92 echo "before inner"
93 printenv.py outer_var
94 inner
95 echo "after inner"
96 printenv.py outer_var
97 }
98 outer
99 # stdout-json: "before inner\nNone\ninner: X\nX\nafter inner\nX\n"
100
101 ### Dependent export setting
102 # FOO is not respected here either.
103 export FOO=foo v=$(printenv.py FOO)
104 echo "v=$v"
105 # stdout: v=None
106
107 ### Exporting a variable doesn't change it
108 old=$PATH
109 export PATH
110 new=$PATH
111 test "$old" = "$new" && echo "not changed"
112 # stdout: not changed
113
114 ### assign to readonly variable
115 # bash doesn't abort unless errexit!
116 readonly foo=bar
117 foo=eggs
118 echo "status=$?" # nothing happens
119 # status: 1
120 # BUG bash stdout: status=1
121 # BUG bash status: 0
122 # OK dash/mksh status: 2
123
124 ### assign to readonly variable - errexit
125 set -o errexit
126 readonly foo=bar
127 foo=eggs
128 echo "status=$?" # nothing happens
129 # status: 1
130 # OK dash/mksh status: 2
131
132 ### Unset a variable
133 foo=bar
134 echo foo=$foo
135 unset foo
136 echo foo=$foo
137 # stdout-json: "foo=bar\nfoo=\n"
138
139 ### Unset exit status
140 V=123
141 unset V
142 echo status=$?
143 # stdout: status=0
144
145 ### Unset nonexistent variable
146 unset ZZZ
147 echo status=$?
148 # stdout: status=0
149
150 ### Unset readonly variable
151 # dash aborts the whole program. I'm also aborting the whole program because
152 # it's a programming error.
153 readonly R=foo
154 unset R
155 echo status=$?
156 # status: 1
157 # stdout-json: ""
158 # OK dash status: 2
159 # BUG mksh/bash stdout-json: "status=1\n"
160 # BUG mksh/bash status: 0
161
162 ### Unset a function without -f
163 f() {
164 echo foo
165 }
166 f
167 unset f
168 f
169 # stdout: foo
170 # status: 127
171 # N-I dash/mksh status: 0
172 # N-I dash/mksh stdout-json: "foo\nfoo\n"
173
174 ### Unset has dynamic scope
175 f() {
176 unset foo
177 }
178 foo=bar
179 echo foo=$foo
180 f
181 echo foo=$foo
182 # stdout-json: "foo=bar\nfoo=\n"
183
184 ### Unset -v
185 foo() {
186 echo "function foo"
187 }
188 foo=bar
189 unset -v foo
190 echo foo=$foo
191 foo
192 # stdout-json: "foo=\nfunction foo\n"
193
194 ### Unset -f
195 foo() {
196 echo "function foo"
197 }
198 foo=bar
199 unset -f foo
200 echo foo=$foo
201 foo
202 echo status=$?
203 # stdout-json: "foo=bar\nstatus=127\n"
204
205 ### Unset array member
206 a=(x y z)
207 unset 'a[1]'
208 echo "${a[@]}" len="${#a[@]}"
209 # stdout: x z len=2
210 # N-I dash status: 2
211 # N-I dash stdout-json: ""
212
213 ### Unset array member with expression
214 i=1
215 a=(w x y z)
216 unset 'a[ i - 1 ]' a[i+1] # note: can't have space between a and [
217 echo "${a[@]}" len="${#a[@]}"
218 # stdout: x z len=2
219 # N-I dash status: 2
220 # N-I dash stdout-json: ""