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: 0
157 ## stdout: status=1
158 ## OK dash status: 2
159 ## OK dash stdout-json: ""
160
161 #### Unset a function without -f
162 f() {
163 echo foo
164 }
165 f
166 unset f
167 f
168 ## stdout: foo
169 ## status: 127
170 ## N-I dash/mksh status: 0
171 ## N-I dash/mksh stdout-json: "foo\nfoo\n"
172
173 #### Unset has dynamic scope
174 f() {
175 unset foo
176 }
177 foo=bar
178 echo foo=$foo
179 f
180 echo foo=$foo
181 ## stdout-json: "foo=bar\nfoo=\n"
182
183 #### Unset -v
184 foo() {
185 echo "function foo"
186 }
187 foo=bar
188 unset -v foo
189 echo foo=$foo
190 foo
191 ## stdout-json: "foo=\nfunction foo\n"
192
193 #### Unset -f
194 foo() {
195 echo "function foo"
196 }
197 foo=bar
198 unset -f foo
199 echo foo=$foo
200 foo
201 echo status=$?
202 ## stdout-json: "foo=bar\nstatus=127\n"
203
204 #### Unset array member
205 a=(x y z)
206 unset 'a[1]'
207 echo "${a[@]}" len="${#a[@]}"
208 ## stdout: x z len=2
209 ## N-I dash status: 2
210 ## N-I dash stdout-json: ""
211
212 #### Unset array member with expression
213 i=1
214 a=(w x y z)
215 unset 'a[ i - 1 ]' a[i+1] # note: can't have space between a and [
216 echo "${a[@]}" len="${#a[@]}"
217 ## stdout: x z len=2
218 ## N-I dash status: 2
219 ## N-I dash stdout-json: ""
220
221 #### Use local twice
222 f() {
223 local foo=bar
224 local foo
225 echo $foo
226 }
227 f
228 ## stdout: bar
229
230 #### Local without variable is still unset!
231 set -o nounset
232 f() {
233 local foo
234 echo "[$foo]"
235 }
236 f
237 ## status: 1
238 ## OK dash status: 2