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: "" |
221 |
|
222 |
### Use local twice |
223 |
f() { |
224 |
local foo=bar |
225 |
local foo |
226 |
echo $foo |
227 |
} |
228 |
f |
229 |
# stdout: bar |
230 |
|
231 |
### Local without variable is still unset! |
232 |
set -o nounset |
233 |
f() { |
234 |
local foo |
235 |
echo "[$foo]" |
236 |
} |
237 |
f |
238 |
# status: 1 |
239 |
# OK dash status: 2 |