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 is NOT exported! (pitfall) |
148 |
|
149 |
# All shells agree on this, but it's very confusing behavior. |
150 |
FOO=foo readonly v=$(printenv.py FOO) |
151 |
echo "v=$v" |
152 |
|
153 |
# bash has probems here: |
154 |
FOO=foo readonly v2=$FOO |
155 |
echo "v2=$v2" |
156 |
|
157 |
## STDOUT: |
158 |
v=None |
159 |
v2=foo |
160 |
## END |
161 |
## BUG bash STDOUT: |
162 |
v=None |
163 |
v2= |
164 |
## END |
165 |
|
166 |
#### assignments / array assignments not interpreted after 'echo' |
167 |
a=1 echo b[0]=2 c=3 |
168 |
## stdout: b[0]=2 c=3 |
169 |
# zsh interprets [0] as some kind of glob |
170 |
## OK zsh stdout-json: "" |
171 |
## OK zsh status: 1 |
172 |
|
173 |
#### dynamic local variables (and splitting) |
174 |
f() { |
175 |
local "$1" # Only x is assigned here |
176 |
echo x=\'$x\' |
177 |
echo a=\'$a\' |
178 |
|
179 |
local $1 # x and a are assigned here |
180 |
echo x=\'$x\' |
181 |
echo a=\'$a\' |
182 |
} |
183 |
f 'x=y a=b' |
184 |
## OK dash/bash/mksh STDOUT: |
185 |
x='y a=b' |
186 |
a='' |
187 |
x='y' |
188 |
a='b' |
189 |
## END |
190 |
# osh and zsh don't do word splitting |
191 |
## STDOUT: |
192 |
x='y a=b' |
193 |
a='' |
194 |
x='y a=b' |
195 |
a='' |
196 |
## END |
197 |
|
198 |
#### readonly x= gives empty string (regression) |
199 |
readonly x= |
200 |
argv.py "$x" |
201 |
## STDOUT: |
202 |
[''] |
203 |
## END |
204 |
|
205 |
#### 'local x' does not set variable |
206 |
set -o nounset |
207 |
f() { |
208 |
local x |
209 |
echo $x |
210 |
} |
211 |
f |
212 |
## status: 1 |
213 |
## OK dash status: 2 |
214 |
## BUG zsh status: 0 |
215 |
|
216 |
#### 'local -a x' does not set variable |
217 |
set -o nounset |
218 |
f() { |
219 |
local -a x |
220 |
echo $x |
221 |
} |
222 |
f |
223 |
## status: 1 |
224 |
## OK dash status: 2 |
225 |
## BUG zsh status: 0 |
226 |
|
227 |
#### 'local x' and then array assignment |
228 |
f() { |
229 |
local x |
230 |
x[3]=foo |
231 |
echo ${x[3]} |
232 |
} |
233 |
f |
234 |
## status: 0 |
235 |
## stdout: foo |
236 |
## N-I dash status: 2 |
237 |
## N-I dash stdout-json: "" |
238 |
## BUG zsh stdout: o |
239 |
|
240 |
#### 'declare -A' and then dict assignment |
241 |
declare -A foo |
242 |
key=bar |
243 |
foo["$key"]=value |
244 |
echo ${foo["bar"]} |
245 |
## status: 0 |
246 |
## stdout: value |
247 |
## N-I dash status: 2 |
248 |
## N-I dash stdout-json: "" |
249 |
## N-I mksh status: 1 |
250 |
## N-I mksh stdout-json: "" |
251 |
|
252 |
#### declare in an if statement |
253 |
# bug caught by my feature detection snippet in bash-completion |
254 |
if ! foo=bar; then |
255 |
echo BAD |
256 |
fi |
257 |
echo $foo |
258 |
if ! eval 'spam=eggs'; then |
259 |
echo BAD |
260 |
fi |
261 |
echo $spam |
262 |
## STDOUT: |
263 |
bar |
264 |
eggs |
265 |
## END |
266 |
|
267 |
|
268 |
#### Modify a temporary binding |
269 |
# (regression for bug found by Michael Greenberg) |
270 |
f() { |
271 |
echo "x before = $x" |
272 |
x=$((x+1)) |
273 |
echo "x after = $x" |
274 |
} |
275 |
x=5 f |
276 |
## STDOUT: |
277 |
x before = 5 |
278 |
x after = 6 |
279 |
## END |
280 |
|
281 |
#### Reveal existence of "temp frame" (All shells disagree here!!!) |
282 |
f() { |
283 |
echo "x=$x" |
284 |
|
285 |
x=mutated-temp # mutate temp frame |
286 |
echo "x=$x" |
287 |
|
288 |
# Declare a new local |
289 |
local x='local' |
290 |
echo "x=$x" |
291 |
|
292 |
# Unset it |
293 |
unset x |
294 |
echo "x=$x" |
295 |
} |
296 |
|
297 |
x=global |
298 |
x=temp-binding f |
299 |
echo "x=$x" |
300 |
|
301 |
## STDOUT: |
302 |
x=temp-binding |
303 |
x=mutated-temp |
304 |
x=local |
305 |
x= |
306 |
x=global |
307 |
## END |
308 |
## BUG dash STDOUT: |
309 |
x=temp-binding |
310 |
x=mutated-temp |
311 |
x=local |
312 |
x= |
313 |
x=mutated-temp |
314 |
## END |
315 |
## BUG bash STDOUT: |
316 |
x=temp-binding |
317 |
x=mutated-temp |
318 |
x=local |
319 |
x=global |
320 |
x=global |
321 |
## END |
322 |
## BUG mksh STDOUT: |
323 |
x=temp-binding |
324 |
x=mutated-temp |
325 |
x=local |
326 |
x=mutated-temp |
327 |
x=mutated-temp |
328 |
## END |
329 |
## BUG yash STDOUT: |
330 |
# yash has no locals |
331 |
x=temp-binding |
332 |
x=mutated-temp |
333 |
x=mutated-temp |
334 |
x= |
335 |
x= |
336 |
## END |
337 |
|
338 |
#### Test above without 'local' (which is not POSIX) |
339 |
f() { |
340 |
echo "x=$x" |
341 |
|
342 |
x=mutated-temp # mutate temp frame |
343 |
echo "x=$x" |
344 |
|
345 |
# Unset it |
346 |
unset x |
347 |
echo "x=$x" |
348 |
} |
349 |
|
350 |
x=global |
351 |
x=temp-binding f |
352 |
echo "x=$x" |
353 |
|
354 |
## STDOUT: |
355 |
x=temp-binding |
356 |
x=mutated-temp |
357 |
x= |
358 |
x=global |
359 |
## END |
360 |
## BUG dash/mksh/yash STDOUT: |
361 |
x=temp-binding |
362 |
x=mutated-temp |
363 |
x= |
364 |
x= |
365 |
## END |
366 |
## BUG bash STDOUT: |
367 |
x=temp-binding |
368 |
x=mutated-temp |
369 |
x=global |
370 |
x=global |
371 |
## END |
372 |
|
373 |
#### Using ${x-default} after unsetting local shadowing a global |
374 |
f() { |
375 |
echo "x=$x" |
376 |
local x='local' |
377 |
echo "x=$x" |
378 |
unset x |
379 |
echo "- operator = ${x-default}" |
380 |
echo ":- operator = ${x:-default}" |
381 |
} |
382 |
x=global |
383 |
f |
384 |
## STDOUT: |
385 |
x=global |
386 |
x=local |
387 |
- operator = default |
388 |
:- operator = default |
389 |
## END |
390 |
## BUG mksh STDOUT: |
391 |
x=global |
392 |
x=local |
393 |
- operator = global |
394 |
:- operator = global |
395 |
## END |
396 |
|
397 |
#### Using ${x-default} after unsetting a temp binding shadowing a global |
398 |
f() { |
399 |
echo "x=$x" |
400 |
local x='local' |
401 |
echo "x=$x" |
402 |
unset x |
403 |
echo "- operator = ${x-default}" |
404 |
echo ":- operator = ${x:-default}" |
405 |
} |
406 |
x=global |
407 |
x=temp-binding f |
408 |
## STDOUT: |
409 |
x=temp-binding |
410 |
x=local |
411 |
- operator = default |
412 |
:- operator = default |
413 |
## END |
414 |
## BUG mksh STDOUT: |
415 |
x=temp-binding |
416 |
x=local |
417 |
- operator = temp-binding |
418 |
:- operator = temp-binding |
419 |
## END |
420 |
## BUG bash STDOUT: |
421 |
x=temp-binding |
422 |
x=local |
423 |
- operator = global |
424 |
:- operator = global |
425 |
## END |
426 |
|
427 |
#### static assignment doesn't split |
428 |
words='a b c' |
429 |
export ex=$words |
430 |
glo=$words |
431 |
readonly ro=$words |
432 |
argv.py "$ex" "$glo" "$ro" |
433 |
|
434 |
## STDOUT: |
435 |
['a b c', 'a b c', 'a b c'] |
436 |
## END |
437 |
## BUG dash STDOUT: |
438 |
['a', 'a b c', 'a'] |
439 |
## END |
440 |
|
441 |
|
442 |
#### aliased assignment doesn't split |
443 |
shopt -s expand_aliases || true |
444 |
words='a b c' |
445 |
alias e=export |
446 |
alias r=readonly |
447 |
e ex=$words |
448 |
r ro=$words |
449 |
argv.py "$ex" "$ro" |
450 |
## BUG dash STDOUT: |
451 |
['a', 'a'] |
452 |
## END |
453 |
## STDOUT: |
454 |
['a b c', 'a b c'] |
455 |
## END |
456 |
|
457 |
|
458 |
#### assignment using dynamic keyword (splits in most shells, not in zsh/osh) |
459 |
words='a b c' |
460 |
e=export |
461 |
r=readonly |
462 |
$e ex=$words |
463 |
$r ro=$words |
464 |
argv.py "$ex" "$ro" |
465 |
|
466 |
# zsh and OSH are smart |
467 |
## STDOUT: |
468 |
['a b c', 'a b c'] |
469 |
## END |
470 |
|
471 |
## OK dash/bash/mksh STDOUT: |
472 |
['a', 'a'] |
473 |
## END |
474 |
|
475 |
|
476 |
#### assignment using dynamic var names doesn't split |
477 |
words='a b c' |
478 |
arg_ex=ex=$words |
479 |
arg_ro=ro=$words |
480 |
|
481 |
# no quotes, this is split of course |
482 |
export $arg_ex |
483 |
readonly $arg_ro |
484 |
|
485 |
argv.py "$ex" "$ro" |
486 |
|
487 |
arg_ex2=ex2=$words |
488 |
arg_ro2=ro2=$words |
489 |
|
490 |
# quotes, no splitting |
491 |
export "$arg_ex2" |
492 |
readonly "$arg_ro2" |
493 |
|
494 |
argv.py "$ex2" "$ro2" |
495 |
|
496 |
## STDOUT: |
497 |
['a b c', 'a b c'] |
498 |
['a b c', 'a b c'] |
499 |
## END |
500 |
## OK dash/bash/mksh STDOUT: |
501 |
['a', 'a'] |
502 |
['a b c', 'a b c'] |
503 |
## END |
504 |
|
505 |
#### assign and glob |
506 |
cd $TMP |
507 |
touch foo=a foo=b |
508 |
foo=* |
509 |
argv "$foo" |
510 |
unset foo |
511 |
|
512 |
export foo=* |
513 |
argv "$foo" |
514 |
unset foo |
515 |
|
516 |
## STDOUT: |
517 |
['*'] |
518 |
['*'] |
519 |
## END |
520 |
## BUG dash STDOUT: |
521 |
['*'] |
522 |
['b'] |
523 |
## END |
524 |
|
525 |
#### declare and glob |
526 |
cd $TMP |
527 |
touch foo=a foo=b |
528 |
typeset foo=* |
529 |
argv "$foo" |
530 |
unset foo |
531 |
## STDOUT: |
532 |
['*'] |
533 |
## END |
534 |
## N-I dash STDOUT: |
535 |
[''] |
536 |
## END |
537 |
|
538 |
#### readonly $x where x='b c' |
539 |
one=a |
540 |
two='b c' |
541 |
readonly $two $one |
542 |
a=new |
543 |
echo status=$? |
544 |
b=new |
545 |
echo status=$? |
546 |
c=new |
547 |
echo status=$? |
548 |
|
549 |
# in OSH and zsh, this is an invalid variable name |
550 |
## status: 1 |
551 |
## stdout-json: "" |
552 |
|
553 |
# most shells make two variable read-only |
554 |
|
555 |
## OK dash/mksh status: 2 |
556 |
## OK bash status: 0 |
557 |
## OK bash STDOUT: |
558 |
status=1 |
559 |
status=1 |
560 |
status=1 |
561 |
## END |
562 |
|
563 |
#### readonly a=(1 2) no_value c=(3 4) makes 'no_value' readonly |
564 |
readonly a=(1 2) no_value c=(3 4) |
565 |
no_value=x |
566 |
## status: 1 |
567 |
## stdout-json: "" |
568 |
## OK dash status: 2 |
569 |
|
570 |
#### export a=1 no_value c=2 |
571 |
no_value=foo |
572 |
export a=1 no_value c=2 |
573 |
printenv.py no_value |
574 |
## STDOUT: |
575 |
foo |
576 |
## END |
577 |
|
578 |
#### local a=loc $var c=loc |
579 |
var='b' |
580 |
b=global |
581 |
echo $b |
582 |
f() { |
583 |
local a=loc $var c=loc |
584 |
argv.py "$a" "$b" "$c" |
585 |
} |
586 |
f |
587 |
## STDOUT: |
588 |
global |
589 |
['loc', '', 'loc'] |
590 |
## END |
591 |
## BUG dash STDOUT: |
592 |
global |
593 |
['loc', 'global', 'loc'] |
594 |
## END |
595 |
|
596 |
#### redirect after assignment builtin (what's going on with dash/bash/mksh here?) |
597 |
readonly x=$(stdout_stderr.py) 2>/dev/null |
598 |
echo done |
599 |
## STDOUT: |
600 |
done |
601 |
## END |
602 |
## STDERR: |
603 |
STDERR |
604 |
## END |
605 |
## BUG zsh stderr-json: "" |
606 |
|
607 |
#### redirect after command sub (like case above but without assignment builtin) |
608 |
echo stdout=$(stdout_stderr.py) 2>/dev/null |
609 |
## STDOUT: |
610 |
stdout=STDOUT |
611 |
## END |
612 |
## STDERR: |
613 |
STDERR |
614 |
## END |
615 |
|
616 |
#### redirect after bare assignment |
617 |
x=$(stdout_stderr.py) 2>/dev/null |
618 |
echo done |
619 |
## STDOUT: |
620 |
done |
621 |
## END |
622 |
## stderr-json: "" |
623 |
## BUG bash STDERR: |
624 |
STDERR |
625 |
## END |
626 |
|
627 |
#### redirect after declare -p |
628 |
case $SH in *dash) exit 99 ;; esac # stderr unpredictable |
629 |
|
630 |
foo=bar |
631 |
typeset -p foo 1>&2 |
632 |
## STDERR: |
633 |
typeset foo=bar |
634 |
## END |
635 |
## OK bash STDERR: |
636 |
declare -- foo="bar" |
637 |
## END |
638 |
## OK osh STDERR: |
639 |
foo |
640 |
## END |
641 |
## N-I dash status: 99 |
642 |
## N-I dash stderr-json: "" |
643 |
## stdout-json: "" |
644 |
|