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:
24 X
25 X
26 X
27 None
28 ## END
29 ## N-I mksh/dash STDOUT:
30 X
31 X
32 ## END
33 ## N-I mksh status: 1
34 ## N-I dash status: 2
35 ## N-I zsh STDOUT:
36 X
37 X
38 X
39 X
40 ## END
41
42 #### export -n undefined is ignored
43 set -o errexit
44 export -n undef
45 echo status=$?
46 ## stdout: status=0
47 ## N-I mksh/dash/zsh stdout-json: ""
48 ## N-I mksh status: 1
49 ## N-I dash status: 2
50 ## N-I zsh status: 1
51
52 #### Export a global variable and unset it
53 f() { export GLOBAL=X; }
54 f
55 echo $GLOBAL
56 printenv.py GLOBAL
57 unset GLOBAL
58 echo $GLOBAL
59 printenv.py GLOBAL
60 ## stdout-json: "X\nX\n\nNone\n"
61
62 #### Export existing global variables
63 G1=g1
64 G2=g2
65 export G1 G2
66 printenv.py G1 G2
67 ## stdout-json: "g1\ng2\n"
68
69 #### Export existing local variable
70 f() {
71 local L1=local1
72 export L1
73 printenv.py L1
74 }
75 f
76 printenv.py L1
77 ## stdout-json: "local1\nNone\n"
78
79 #### Export a local that shadows a global
80 V=global
81 f() {
82 local V=local1
83 export V
84 printenv.py V
85 }
86 f
87 printenv.py V # exported local out of scope; global isn't exported yet
88 export V
89 printenv.py V # now it's exported
90 ## stdout-json: "local1\nNone\nglobal\n"
91
92 #### Export a variable before defining it
93 export U
94 U=u
95 printenv.py U
96 ## stdout: u
97
98 #### Exporting a parent func variable (dynamic scope)
99 # The algorithm is to walk up the stack and export that one.
100 inner() {
101 export outer_var
102 echo "inner: $outer_var"
103 printenv.py outer_var
104 }
105 outer() {
106 local outer_var=X
107 echo "before inner"
108 printenv.py outer_var
109 inner
110 echo "after inner"
111 printenv.py outer_var
112 }
113 outer
114 ## stdout-json: "before inner\nNone\ninner: X\nX\nafter inner\nX\n"
115
116 #### Dependent export setting
117 # FOO is not respected here either.
118 export FOO=foo v=$(printenv.py FOO)
119 echo "v=$v"
120 ## stdout: v=None
121
122 #### Exporting a variable doesn't change it
123 old=$PATH
124 export PATH
125 new=$PATH
126 test "$old" = "$new" && echo "not changed"
127 ## stdout: not changed
128
129 #### assign to readonly variable
130 # bash doesn't abort unless errexit!
131 readonly foo=bar
132 foo=eggs
133 echo "status=$?" # nothing happens
134 ## status: 1
135 ## BUG bash stdout: status=1
136 ## BUG bash status: 0
137 ## OK dash/mksh status: 2
138
139 #### assign to readonly variable - errexit
140 set -o errexit
141 readonly foo=bar
142 foo=eggs
143 echo "status=$?" # nothing happens
144 ## status: 1
145 ## OK dash/mksh status: 2
146
147 #### Unset a variable
148 foo=bar
149 echo foo=$foo
150 unset foo
151 echo foo=$foo
152 ## stdout-json: "foo=bar\nfoo=\n"
153
154 #### Unset exit status
155 V=123
156 unset V
157 echo status=$?
158 ## stdout: status=0
159
160 #### Unset nonexistent variable
161 unset ZZZ
162 echo status=$?
163 ## stdout: status=0
164
165 #### Unset readonly variable
166 # dash and zsh abort the whole program. OSH doesn't?
167 readonly R=foo
168 unset R
169 echo status=$?
170 ## status: 0
171 ## stdout: status=1
172 ## OK dash status: 2
173 ## OK dash stdout-json: ""
174 ## OK zsh status: 1
175 ## OK zsh stdout-json: ""
176
177 #### Unset a function without -f
178 f() {
179 echo foo
180 }
181 f
182 unset f
183 f
184 ## stdout: foo
185 ## status: 127
186 ## N-I dash/mksh/zsh status: 0
187 ## N-I dash/mksh/zsh STDOUT:
188 foo
189 foo
190 ## END
191
192 #### Unset has dynamic scope
193 f() {
194 unset foo
195 }
196 foo=bar
197 echo foo=$foo
198 f
199 echo foo=$foo
200 ## stdout-json: "foo=bar\nfoo=\n"
201
202 #### Unset invalid variable name
203 unset %
204 echo status=$?
205 ## STDOUT:
206 status=2
207 ## END
208 ## OK bash/mksh STDOUT:
209 status=1
210 ## END
211 ## BUG zsh STDOUT:
212 status=0
213 ## END
214 # dash does a hard failure!
215 ## OK dash stdout-json: ""
216 ## OK dash status: 2
217
218 #### Unset nonexistent variable
219 unset _nonexistent__
220 echo status=$?
221 ## STDOUT:
222 status=0
223 ## END
224
225 #### Unset -v
226 foo() {
227 echo "function foo"
228 }
229 foo=bar
230 unset -v foo
231 echo foo=$foo
232 foo
233 ## stdout-json: "foo=\nfunction foo\n"
234
235 #### Unset -f
236 foo() {
237 echo "function foo"
238 }
239 foo=bar
240 unset -f foo
241 echo foo=$foo
242 foo
243 echo status=$?
244 ## stdout-json: "foo=bar\nstatus=127\n"
245
246 #### Unset array member
247 a=(x y z)
248 unset 'a[1]'
249 echo "${a[@]}" len="${#a[@]}"
250 ## stdout: x z len=2
251 ## N-I dash status: 2
252 ## N-I dash stdout-json: ""
253 ## OK zsh stdout-json: " y z len=3\n"
254
255 #### Unset array member with expression
256 i=1
257 a=(w x y z)
258 unset 'a[ i - 1 ]' a[i+1] # note: can't have space between a and [
259 echo "${a[@]}" len="${#a[@]}"
260 ## stdout: x z len=2
261 ## N-I dash status: 2
262 ## N-I dash stdout-json: ""
263 ## N-I zsh status: 1
264 ## N-I zsh stdout-json: ""
265
266 #### Use local twice
267 f() {
268 local foo=bar
269 local foo
270 echo $foo
271 }
272 f
273 ## stdout: bar
274 ## BUG zsh STDOUT:
275 foo=bar
276 bar
277 ## END
278
279 #### Local without variable is still unset!
280 set -o nounset
281 f() {
282 local foo
283 echo "[$foo]"
284 }
285 f
286 ## stdout-json: ""
287 ## status: 1
288 ## OK dash status: 2
289 # zsh doesn't support nounset?
290 ## BUG zsh stdout: []
291 ## BUG zsh status: 0
292
293 #### local after readonly
294 f() {
295 readonly y
296 local x=1 y=$(( x ))
297 echo y=$y
298 }
299 f
300 ## stdout-json: ""
301 ## status: 1
302 ## OK dash status: 2
303 ## BUG bash stdout: y=
304 ## BUG bash status: 0
305 ## BUG mksh stdout: y=0
306 ## BUG mksh status: 0
307