1 #!/usr/bin/env bash
2
3 #### Env value doesn't persist
4 FOO=foo printenv.py FOO
5 echo -$FOO-
6 ## STDOUT:
7 foo
8 --
9 ## END
10
11 #### Env value with equals
12 FOO=foo=foo printenv.py FOO
13 ## stdout: foo=foo
14
15 #### Env binding can use preceding bindings, but not subsequent ones
16 # This means that for ASSIGNMENT_WORD, on the RHS you invoke the parser again!
17 # Could be any kind of quoted string.
18 FOO="foo" BAR="[$FOO][$BAZ]" BAZ=baz printenv.py FOO BAR BAZ
19 ## STDOUT:
20 foo
21 [foo][]
22 baz
23 ## BUG mksh STDOUT:
24 foo
25 [][]
26 baz
27 ## END
28
29 #### Env value with two quotes
30 FOO='foo'"adjacent" printenv.py FOO
31 ## stdout: fooadjacent
32
33 #### Env value with escaped <
34 FOO=foo\<foo printenv.py FOO
35 ## stdout: foo<foo
36
37 #### FOO=foo echo [foo]
38 FOO=foo echo "[$foo]"
39 ## stdout: []
40
41 #### FOO=foo func
42 func() {
43 echo "[$FOO]"
44 }
45 FOO=foo func
46 ## stdout: [foo]
47
48 #### Multiple temporary envs on the stack
49 g() {
50 echo "$F" "$G1" "$G2"
51 echo '--- g() ---'
52 P=p printenv.py F G1 G2 A P
53 }
54 f() {
55 # NOTE: G1 doesn't pick up binding f, but G2 picks up a.
56 # I don't quite understand why this is, but bash and OSH agree!
57 G1=[$f] G2=[$a] g
58 echo '--- f() ---'
59 printenv.py F G1 G2 A P
60 }
61 a=A
62 F=f f
63 ## STDOUT:
64 f [] [A]
65 --- g() ---
66 f
67 []
68 [A]
69 None
70 p
71 --- f() ---
72 f
73 None
74 None
75 None
76 None
77 ## END
78 ## OK mksh STDOUT:
79 # G1 and G2 somehow persist. I think that is a bug. They should be local to
80 # the G call.
81 f [] [A]
82 --- g() ---
83 f
84 []
85 [A]
86 None
87 p
88 --- f() ---
89 f
90 []
91 [A]
92 None
93 None
94 ## END
95 ## BUG dash STDOUT:
96 # dash sets even less stuff. Doesn't appear correct.
97 f [] [A]
98 --- g() ---
99 None
100 None
101 None
102 None
103 p
104 --- f() ---
105 None
106 None
107 None
108 None
109 None
110 ## END
111
112 #### Escaped = in command name
113 # foo=bar is in the 'spec/bin' dir.
114 foo\=bar
115 ## stdout: HI
116
117 #### Env binding not allowed before compound command
118 # bash gives exit code 2 for syntax error, because of 'do'.
119 # dash gives 0 because there is stuff after for? Should really give an error.
120 # mksh gives acceptable error of 1.
121 FOO=bar for i in a b; do printenv.py $FOO; done
122 ## BUG dash status: 0
123 ## OK mksh/zsh status: 1
124 ## status: 2
125
126 #### Trying to run keyword 'for'
127 FOO=bar for
128 ## status: 127
129 ## OK zsh status: 1
130
131 #### Empty env binding
132 EMPTY= printenv.py EMPTY
133 ## stdout:
134
135 #### Assignment doesn't do word splitting
136 words='one two'
137 a=$words
138 argv.py "$a"
139 ## stdout: ['one two']
140
141 #### Assignment doesn't do glob expansion
142 touch _tmp/z.Z _tmp/zz.Z
143 a=_tmp/*.Z
144 argv.py "$a"
145 ## stdout: ['_tmp/*.Z']
146
147 #### Env binding in readonly/declare disallowed
148 # I'm disallowing this in the oil shell, because it doesn't work in bash!
149 # (v=None vs v=foo)
150 # assert status 2 for parse error, but allow stdout v=None/status 0 for
151 # existing implementations.
152 FOO=foo readonly v=$(printenv.py FOO)
153 echo "v=$v"
154 ## OK bash/dash/mksh/zsh stdout: v=None
155 ## OK bash/dash/mksh/zsh status: 0
156 ## status: 2
157
158
159 #### assignments / array assignments not interpreted after 'echo'
160 a=1 echo b[0]=2 c=3
161 ## stdout: b[0]=2 c=3
162 # zsh interprets [0] as some kind of glob
163 ## OK zsh stdout-json: ""
164 ## OK zsh status: 1
165
166 #### dynamic local variables
167 f() {
168 local "$1" # Only x is assigned here
169 echo -$x-
170 echo -$a-
171
172 local $1 # x and a are assigned here
173 echo -$x-
174 echo -$a-
175 }
176 f 'x=y a=b'
177 ## STDOUT:
178 -y a=b-
179 --
180 -y-
181 -b-
182 ## END
183 # zsh doesn't do word splitting
184 ## OK zsh STDOUT:
185 -y a=b-
186 --
187 -y a=b-
188 --
189 ## END
190
191 #### 'local x' does not set variable
192 set -o nounset
193 f() {
194 local x
195 echo $x
196 }
197 f
198 ## status: 1
199 ## OK dash status: 2
200 ## BUG zsh status: 0
201
202 #### 'local -a x' does not set variable
203 set -o nounset
204 f() {
205 local -a x
206 echo $x
207 }
208 f
209 ## status: 1
210 ## OK dash status: 2
211 ## BUG zsh status: 0
212
213 #### 'local x' and then array assignment
214 f() {
215 local x
216 x[3]=foo
217 echo ${x[3]}
218 }
219 f
220 ## status: 0
221 ## stdout: foo
222 ## N-I dash status: 2
223 ## N-I dash stdout-json: ""
224 ## BUG zsh stdout: o
225
226 #### 'declare -A' and then dict assignment
227 declare -A foo
228 key=bar
229 foo["$key"]=value
230 echo ${foo["bar"]}
231 ## status: 0
232 ## stdout: value
233 ## N-I dash status: 2
234 ## N-I dash stdout-json: ""
235 ## N-I mksh status: 1
236 ## N-I mksh stdout-json: ""
237
238 #### declare in an if statement
239 # bug caught by my feature detection snippet in bash-completion
240 if ! foo=bar; then
241 echo BAD
242 fi
243 echo $foo
244 if ! eval 'spam=eggs'; then
245 echo BAD
246 fi
247 echo $spam
248 ## STDOUT:
249 bar
250 eggs
251 ## END
252
253
254 #### Modify a temporary binding
255 # (regression for bug found by Michael Greenberg)
256 f() {
257 echo "x before = $x"
258 x=$((x+1))
259 echo "x after = $x"
260 }
261 x=5 f
262 ## STDOUT:
263 x before = 5
264 x after = 6
265 ## END
266
267 #### Reveal existence of "temp frame" (All shells disagree here!!!)
268 f() {
269 echo "x=$x"
270
271 x=mutated-temp # mutate temp frame
272 echo "x=$x"
273
274 # Declare a new local
275 local x='local'
276 echo "x=$x"
277
278 # Unset it
279 unset x
280 echo "x=$x"
281 }
282
283 x=global
284 x=temp-binding f
285 echo "x=$x"
286
287 ## STDOUT:
288 x=temp-binding
289 x=mutated-temp
290 x=local
291 x=
292 x=global
293 ## END
294 ## BUG dash STDOUT:
295 x=temp-binding
296 x=mutated-temp
297 x=local
298 x=
299 x=mutated-temp
300 ## END
301 ## BUG bash STDOUT:
302 x=temp-binding
303 x=mutated-temp
304 x=local
305 x=global
306 x=global
307 ## END
308 ## BUG mksh STDOUT:
309 x=temp-binding
310 x=mutated-temp
311 x=local
312 x=mutated-temp
313 x=mutated-temp
314 ## END
315 ## BUG yash STDOUT:
316 # yash has no locals
317 x=temp-binding
318 x=mutated-temp
319 x=mutated-temp
320 x=
321 x=
322 ## END
323
324 #### Test above without 'local' (which is not POSIX)
325 f() {
326 echo "x=$x"
327
328 x=mutated-temp # mutate temp frame
329 echo "x=$x"
330
331 # Unset it
332 unset x
333 echo "x=$x"
334 }
335
336 x=global
337 x=temp-binding f
338 echo "x=$x"
339
340 ## STDOUT:
341 x=temp-binding
342 x=mutated-temp
343 x=
344 x=global
345 ## END
346 ## BUG dash/mksh/yash STDOUT:
347 x=temp-binding
348 x=mutated-temp
349 x=
350 x=
351 ## END
352 ## BUG bash STDOUT:
353 x=temp-binding
354 x=mutated-temp
355 x=global
356 x=global
357 ## END
358
359 #### Using ${x-default} after unsetting local shadowing a global
360 f() {
361 echo "x=$x"
362 local x='local'
363 echo "x=$x"
364 unset x
365 echo "- operator = ${x-default}"
366 echo ":- operator = ${x:-default}"
367 }
368 x=global
369 f
370 ## STDOUT:
371 x=global
372 x=local
373 - operator = default
374 :- operator = default
375 ## END
376 ## BUG mksh STDOUT:
377 x=global
378 x=local
379 - operator = global
380 :- operator = global
381 ## END
382
383 #### Using ${x-default} after unsetting a temp binding shadowing a global
384 f() {
385 echo "x=$x"
386 local x='local'
387 echo "x=$x"
388 unset x
389 echo "- operator = ${x-default}"
390 echo ":- operator = ${x:-default}"
391 }
392 x=global
393 x=temp-binding f
394 ## STDOUT:
395 x=temp-binding
396 x=local
397 - operator = default
398 :- operator = default
399 ## END
400 ## BUG mksh STDOUT:
401 x=temp-binding
402 x=local
403 - operator = temp-binding
404 :- operator = temp-binding
405 ## END
406 ## BUG bash STDOUT:
407 x=temp-binding
408 x=local
409 - operator = global
410 :- operator = global
411 ## END