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 |