1 |
|
2 |
#### Env value doesn't persist |
3 |
FOO=foo printenv.py FOO |
4 |
echo -$FOO- |
5 |
## STDOUT: |
6 |
foo |
7 |
-- |
8 |
## END |
9 |
|
10 |
#### Env value with equals |
11 |
FOO=foo=foo printenv.py FOO |
12 |
## stdout: foo=foo |
13 |
|
14 |
#### Env binding can use preceding bindings, but not subsequent ones |
15 |
# This means that for ASSIGNMENT_WORD, on the RHS you invoke the parser again! |
16 |
# Could be any kind of quoted string. |
17 |
FOO="foo" BAR="[$FOO][$BAZ]" BAZ=baz printenv.py FOO BAR BAZ |
18 |
## STDOUT: |
19 |
foo |
20 |
[foo][] |
21 |
baz |
22 |
## BUG mksh STDOUT: |
23 |
foo |
24 |
[][] |
25 |
baz |
26 |
## END |
27 |
|
28 |
#### Env value with two quotes |
29 |
FOO='foo'"adjacent" printenv.py FOO |
30 |
## stdout: fooadjacent |
31 |
|
32 |
#### Env value with escaped < |
33 |
FOO=foo\<foo printenv.py FOO |
34 |
## stdout: foo<foo |
35 |
|
36 |
#### FOO=foo echo [foo] |
37 |
FOO=foo echo "[$foo]" |
38 |
## stdout: [] |
39 |
|
40 |
#### FOO=foo fun |
41 |
fun() { |
42 |
echo "[$FOO]" |
43 |
} |
44 |
FOO=foo fun |
45 |
## stdout: [foo] |
46 |
|
47 |
#### Multiple temporary envs on the stack |
48 |
g() { |
49 |
echo "$F" "$G1" "$G2" |
50 |
echo '--- g() ---' |
51 |
P=p printenv.py F G1 G2 A P |
52 |
} |
53 |
f() { |
54 |
# NOTE: G1 doesn't pick up binding f, but G2 picks up a. |
55 |
# I don't quite understand why this is, but bash and OSH agree! |
56 |
G1=[$f] G2=[$a] g |
57 |
echo '--- f() ---' |
58 |
printenv.py F G1 G2 A P |
59 |
} |
60 |
a=A |
61 |
F=f f |
62 |
## STDOUT: |
63 |
f [] [A] |
64 |
--- g() --- |
65 |
f |
66 |
[] |
67 |
[A] |
68 |
None |
69 |
p |
70 |
--- f() --- |
71 |
f |
72 |
None |
73 |
None |
74 |
None |
75 |
None |
76 |
## END |
77 |
## OK mksh STDOUT: |
78 |
# G1 and G2 somehow persist. I think that is a bug. They should be local to |
79 |
# the G call. |
80 |
f [] [A] |
81 |
--- g() --- |
82 |
f |
83 |
[] |
84 |
[A] |
85 |
None |
86 |
p |
87 |
--- f() --- |
88 |
f |
89 |
[] |
90 |
[A] |
91 |
None |
92 |
None |
93 |
## END |
94 |
## BUG dash STDOUT: |
95 |
# dash sets even less stuff. Doesn't appear correct. |
96 |
f [] [A] |
97 |
--- g() --- |
98 |
None |
99 |
None |
100 |
None |
101 |
None |
102 |
p |
103 |
--- f() --- |
104 |
None |
105 |
None |
106 |
None |
107 |
None |
108 |
None |
109 |
## END |
110 |
|
111 |
#### Escaped = in command name |
112 |
# foo=bar is in the 'spec/bin' dir. |
113 |
foo\=bar |
114 |
## stdout: HI |
115 |
|
116 |
#### Env binding not allowed before compound command |
117 |
# bash gives exit code 2 for syntax error, because of 'do'. |
118 |
# dash gives 0 because there is stuff after for? Should really give an error. |
119 |
# mksh gives acceptable error of 1. |
120 |
FOO=bar for i in a b; do printenv.py $FOO; done |
121 |
## status: 2 |
122 |
## OK mksh/zsh status: 1 |
123 |
|
124 |
#### Trying to run keyword 'for' |
125 |
FOO=bar for |
126 |
## status: 127 |
127 |
## OK zsh status: 1 |
128 |
|
129 |
#### Empty env binding |
130 |
EMPTY= printenv.py EMPTY |
131 |
## stdout: |
132 |
|
133 |
#### Assignment doesn't do word splitting |
134 |
words='one two' |
135 |
a=$words |
136 |
argv.py "$a" |
137 |
## stdout: ['one two'] |
138 |
|
139 |
#### Assignment doesn't do glob expansion |
140 |
touch _tmp/z.Z _tmp/zz.Z |
141 |
a=_tmp/*.Z |
142 |
argv.py "$a" |
143 |
## stdout: ['_tmp/*.Z'] |
144 |
|
145 |
#### Env binding in readonly/declare is NOT exported! (pitfall) |
146 |
|
147 |
# All shells agree on this, but it's very confusing behavior. |
148 |
FOO=foo readonly v=$(printenv.py FOO) |
149 |
echo "v=$v" |
150 |
|
151 |
# bash has probems here: |
152 |
FOO=foo readonly v2=$FOO |
153 |
echo "v2=$v2" |
154 |
|
155 |
## STDOUT: |
156 |
v=None |
157 |
v2=foo |
158 |
## END |
159 |
## BUG bash STDOUT: |
160 |
v=None |
161 |
v2= |
162 |
## END |
163 |
|
164 |
#### assignments / array assignments not interpreted after 'echo' |
165 |
a=1 echo b[0]=2 c=3 |
166 |
## stdout: b[0]=2 c=3 |
167 |
# zsh interprets [0] as some kind of glob |
168 |
## OK zsh stdout-json: "" |
169 |
## OK zsh status: 1 |
170 |
|
171 |
#### dynamic local variables (and splitting) |
172 |
f() { |
173 |
local "$1" # Only x is assigned here |
174 |
echo x=\'$x\' |
175 |
echo a=\'$a\' |
176 |
|
177 |
local $1 # x and a are assigned here |
178 |
echo x=\'$x\' |
179 |
echo a=\'$a\' |
180 |
} |
181 |
f 'x=y a=b' |
182 |
## OK dash/bash/mksh STDOUT: |
183 |
x='y a=b' |
184 |
a='' |
185 |
x='y' |
186 |
a='b' |
187 |
## END |
188 |
# osh and zsh don't do word splitting |
189 |
## STDOUT: |
190 |
x='y a=b' |
191 |
a='' |
192 |
x='y a=b' |
193 |
a='' |
194 |
## END |
195 |
|
196 |
#### readonly x= gives empty string (regression) |
197 |
readonly x= |
198 |
argv.py "$x" |
199 |
## STDOUT: |
200 |
[''] |
201 |
## END |
202 |
|
203 |
#### 'local x' does not set variable |
204 |
set -o nounset |
205 |
f() { |
206 |
local x |
207 |
echo $x |
208 |
} |
209 |
f |
210 |
## status: 1 |
211 |
## OK dash status: 2 |
212 |
## BUG zsh status: 0 |
213 |
|
214 |
#### 'local -a x' does not set variable |
215 |
set -o nounset |
216 |
f() { |
217 |
local -a x |
218 |
echo $x |
219 |
} |
220 |
f |
221 |
## status: 1 |
222 |
## OK dash status: 2 |
223 |
## BUG zsh status: 0 |
224 |
|
225 |
#### 'local x' and then array assignment |
226 |
f() { |
227 |
local x |
228 |
x[3]=foo |
229 |
echo ${x[3]} |
230 |
} |
231 |
f |
232 |
## status: 0 |
233 |
## stdout: foo |
234 |
## N-I dash status: 2 |
235 |
## N-I dash stdout-json: "" |
236 |
## BUG zsh stdout: o |
237 |
|
238 |
#### 'declare -A' and then dict assignment |
239 |
declare -A foo |
240 |
key=bar |
241 |
foo["$key"]=value |
242 |
echo ${foo["bar"]} |
243 |
## status: 0 |
244 |
## stdout: value |
245 |
## N-I dash status: 2 |
246 |
## N-I dash stdout-json: "" |
247 |
## N-I mksh status: 1 |
248 |
## N-I mksh stdout-json: "" |
249 |
|
250 |
#### declare in an if statement |
251 |
# bug caught by my feature detection snippet in bash-completion |
252 |
if ! foo=bar; then |
253 |
echo BAD |
254 |
fi |
255 |
echo $foo |
256 |
if ! eval 'spam=eggs'; then |
257 |
echo BAD |
258 |
fi |
259 |
echo $spam |
260 |
## STDOUT: |
261 |
bar |
262 |
eggs |
263 |
## END |
264 |
|
265 |
|
266 |
#### Modify a temporary binding |
267 |
# (regression for bug found by Michael Greenberg) |
268 |
f() { |
269 |
echo "x before = $x" |
270 |
x=$((x+1)) |
271 |
echo "x after = $x" |
272 |
} |
273 |
x=5 f |
274 |
## STDOUT: |
275 |
x before = 5 |
276 |
x after = 6 |
277 |
## END |
278 |
|
279 |
#### Reveal existence of "temp frame" (All shells disagree here!!!) |
280 |
f() { |
281 |
echo "x=$x" |
282 |
|
283 |
x=mutated-temp # mutate temp frame |
284 |
echo "x=$x" |
285 |
|
286 |
# Declare a new local |
287 |
local x='local' |
288 |
echo "x=$x" |
289 |
|
290 |
# Unset it |
291 |
unset x |
292 |
echo "x=$x" |
293 |
} |
294 |
|
295 |
x=global |
296 |
x=temp-binding f |
297 |
echo "x=$x" |
298 |
|
299 |
## STDOUT: |
300 |
x=temp-binding |
301 |
x=mutated-temp |
302 |
x=local |
303 |
x=mutated-temp |
304 |
x=global |
305 |
## END |
306 |
## OK dash/zsh STDOUT: |
307 |
x=temp-binding |
308 |
x=mutated-temp |
309 |
x=local |
310 |
x= |
311 |
x=global |
312 |
## END |
313 |
## BUG bash STDOUT: |
314 |
x=temp-binding |
315 |
x=mutated-temp |
316 |
x=local |
317 |
x=global |
318 |
x=global |
319 |
## END |
320 |
## BUG mksh STDOUT: |
321 |
x=temp-binding |
322 |
x=mutated-temp |
323 |
x=local |
324 |
x=mutated-temp |
325 |
x=mutated-temp |
326 |
## END |
327 |
## BUG yash STDOUT: |
328 |
# yash has no locals |
329 |
x=temp-binding |
330 |
x=mutated-temp |
331 |
x=mutated-temp |
332 |
x= |
333 |
x= |
334 |
## END |
335 |
|
336 |
#### Test above without 'local' (which is not POSIX) |
337 |
f() { |
338 |
echo "x=$x" |
339 |
|
340 |
x=mutated-temp # mutate temp frame |
341 |
echo "x=$x" |
342 |
|
343 |
# Unset it |
344 |
unset x |
345 |
echo "x=$x" |
346 |
} |
347 |
|
348 |
x=global |
349 |
x=temp-binding f |
350 |
echo "x=$x" |
351 |
|
352 |
## OK dash/zsh STDOUT: |
353 |
x=temp-binding |
354 |
x=mutated-temp |
355 |
x= |
356 |
x=global |
357 |
## END |
358 |
## BUG mksh/yash STDOUT: |
359 |
x=temp-binding |
360 |
x=mutated-temp |
361 |
x= |
362 |
x= |
363 |
## END |
364 |
## STDOUT: |
365 |
x=temp-binding |
366 |
x=mutated-temp |
367 |
x=global |
368 |
x=global |
369 |
## END |
370 |
|
371 |
#### Using ${x-default} after unsetting local shadowing a global |
372 |
f() { |
373 |
echo "x=$x" |
374 |
local x='local' |
375 |
echo "x=$x" |
376 |
unset x |
377 |
echo "- operator = ${x-default}" |
378 |
echo ":- operator = ${x:-default}" |
379 |
} |
380 |
x=global |
381 |
f |
382 |
## OK dash/bash/zsh STDOUT: |
383 |
x=global |
384 |
x=local |
385 |
- operator = default |
386 |
:- operator = default |
387 |
## END |
388 |
## STDOUT: |
389 |
x=global |
390 |
x=local |
391 |
- operator = global |
392 |
:- operator = global |
393 |
## END |
394 |
|
395 |
#### Using ${x-default} after unsetting a temp binding shadowing a global |
396 |
f() { |
397 |
echo "x=$x" |
398 |
local x='local' |
399 |
echo "x=$x" |
400 |
unset x |
401 |
echo "- operator = ${x-default}" |
402 |
echo ":- operator = ${x:-default}" |
403 |
} |
404 |
x=global |
405 |
x=temp-binding f |
406 |
## OK dash/zsh STDOUT: |
407 |
x=temp-binding |
408 |
x=local |
409 |
- operator = default |
410 |
:- operator = default |
411 |
## END |
412 |
## STDOUT: |
413 |
x=temp-binding |
414 |
x=local |
415 |
- operator = temp-binding |
416 |
:- operator = temp-binding |
417 |
## END |
418 |
## BUG bash STDOUT: |
419 |
x=temp-binding |
420 |
x=local |
421 |
- operator = global |
422 |
:- operator = global |
423 |
## END |
424 |
|
425 |
#### static assignment doesn't split |
426 |
words='a b c' |
427 |
export ex=$words |
428 |
glo=$words |
429 |
readonly ro=$words |
430 |
argv.py "$ex" "$glo" "$ro" |
431 |
|
432 |
## STDOUT: |
433 |
['a b c', 'a b c', 'a b c'] |
434 |
## END |
435 |
## BUG dash STDOUT: |
436 |
['a', 'a b c', 'a'] |
437 |
## END |
438 |
|
439 |
|
440 |
#### aliased assignment doesn't split |
441 |
shopt -s expand_aliases || true |
442 |
words='a b c' |
443 |
alias e=export |
444 |
alias r=readonly |
445 |
e ex=$words |
446 |
r ro=$words |
447 |
argv.py "$ex" "$ro" |
448 |
## BUG dash STDOUT: |
449 |
['a', 'a'] |
450 |
## END |
451 |
## STDOUT: |
452 |
['a b c', 'a b c'] |
453 |
## END |
454 |
|
455 |
|
456 |
#### assignment using dynamic keyword (splits in most shells, not in zsh/osh) |
457 |
words='a b c' |
458 |
e=export |
459 |
r=readonly |
460 |
$e ex=$words |
461 |
$r ro=$words |
462 |
argv.py "$ex" "$ro" |
463 |
|
464 |
# zsh and OSH are smart |
465 |
## STDOUT: |
466 |
['a b c', 'a b c'] |
467 |
## END |
468 |
|
469 |
## OK dash/bash/mksh STDOUT: |
470 |
['a', 'a'] |
471 |
## END |
472 |
|
473 |
|
474 |
#### assignment using dynamic var names doesn't split |
475 |
words='a b c' |
476 |
arg_ex=ex=$words |
477 |
arg_ro=ro=$words |
478 |
|
479 |
# no quotes, this is split of course |
480 |
export $arg_ex |
481 |
readonly $arg_ro |
482 |
|
483 |
argv.py "$ex" "$ro" |
484 |
|
485 |
arg_ex2=ex2=$words |
486 |
arg_ro2=ro2=$words |
487 |
|
488 |
# quotes, no splitting |
489 |
export "$arg_ex2" |
490 |
readonly "$arg_ro2" |
491 |
|
492 |
argv.py "$ex2" "$ro2" |
493 |
|
494 |
## STDOUT: |
495 |
['a b c', 'a b c'] |
496 |
['a b c', 'a b c'] |
497 |
## END |
498 |
## OK dash/bash/mksh STDOUT: |
499 |
['a', 'a'] |
500 |
['a b c', 'a b c'] |
501 |
## END |
502 |
|
503 |
#### assign and glob |
504 |
cd $TMP |
505 |
touch foo=a foo=b |
506 |
foo=* |
507 |
argv.py "$foo" |
508 |
unset foo |
509 |
|
510 |
export foo=* |
511 |
argv.py "$foo" |
512 |
unset foo |
513 |
|
514 |
## STDOUT: |
515 |
['*'] |
516 |
['*'] |
517 |
## END |
518 |
## BUG dash STDOUT: |
519 |
['*'] |
520 |
['b'] |
521 |
## END |
522 |
|
523 |
#### declare and glob |
524 |
cd $TMP |
525 |
touch foo=a foo=b |
526 |
typeset foo=* |
527 |
argv.py "$foo" |
528 |
unset foo |
529 |
## STDOUT: |
530 |
['*'] |
531 |
## END |
532 |
## N-I dash STDOUT: |
533 |
[''] |
534 |
## END |
535 |
|
536 |
#### readonly $x where x='b c' |
537 |
one=a |
538 |
two='b c' |
539 |
readonly $two $one |
540 |
a=new |
541 |
echo status=$? |
542 |
b=new |
543 |
echo status=$? |
544 |
c=new |
545 |
echo status=$? |
546 |
|
547 |
# in OSH and zsh, this is an invalid variable name |
548 |
## status: 1 |
549 |
## stdout-json: "" |
550 |
|
551 |
# most shells make two variable read-only |
552 |
|
553 |
## OK dash/mksh status: 2 |
554 |
## OK bash status: 0 |
555 |
## OK bash STDOUT: |
556 |
status=1 |
557 |
status=1 |
558 |
status=1 |
559 |
## END |
560 |
|
561 |
#### readonly a=(1 2) no_value c=(3 4) makes 'no_value' readonly |
562 |
readonly a=(1 2) no_value c=(3 4) |
563 |
no_value=x |
564 |
## status: 1 |
565 |
## stdout-json: "" |
566 |
## OK dash status: 2 |
567 |
|
568 |
#### export a=1 no_value c=2 |
569 |
no_value=foo |
570 |
export a=1 no_value c=2 |
571 |
printenv.py no_value |
572 |
## STDOUT: |
573 |
foo |
574 |
## END |
575 |
|
576 |
#### local a=loc $var c=loc |
577 |
var='b' |
578 |
b=global |
579 |
echo $b |
580 |
f() { |
581 |
local a=loc $var c=loc |
582 |
argv.py "$a" "$b" "$c" |
583 |
} |
584 |
f |
585 |
## STDOUT: |
586 |
global |
587 |
['loc', '', 'loc'] |
588 |
## END |
589 |
## BUG dash STDOUT: |
590 |
global |
591 |
['loc', 'global', 'loc'] |
592 |
## END |
593 |
|
594 |
#### redirect after assignment builtin (what's going on with dash/bash/mksh here?) |
595 |
readonly x=$(stdout_stderr.py) 2>/dev/null |
596 |
echo done |
597 |
## STDOUT: |
598 |
done |
599 |
## END |
600 |
## STDERR: |
601 |
STDERR |
602 |
## END |
603 |
## BUG zsh stderr-json: "" |
604 |
|
605 |
#### redirect after command sub (like case above but without assignment builtin) |
606 |
echo stdout=$(stdout_stderr.py) 2>/dev/null |
607 |
## STDOUT: |
608 |
stdout=STDOUT |
609 |
## END |
610 |
## STDERR: |
611 |
STDERR |
612 |
## END |
613 |
|
614 |
#### redirect after bare assignment |
615 |
x=$(stdout_stderr.py) 2>/dev/null |
616 |
echo done |
617 |
## STDOUT: |
618 |
done |
619 |
## END |
620 |
## stderr-json: "" |
621 |
## BUG bash STDERR: |
622 |
STDERR |
623 |
## END |
624 |
|
625 |
#### redirect after declare -p |
626 |
case $SH in *dash) exit 99 ;; esac # stderr unpredictable |
627 |
|
628 |
foo=bar |
629 |
typeset -p foo 1>&2 |
630 |
|
631 |
# zsh and mksh agree on exact output, which we don't really care about |
632 |
## STDERR: |
633 |
typeset foo=bar |
634 |
## END |
635 |
## OK bash STDERR: |
636 |
declare -- foo="bar" |
637 |
## END |
638 |
## OK osh STDERR: |
639 |
declare -- foo=bar |
640 |
## END |
641 |
## N-I dash status: 99 |
642 |
## N-I dash stderr-json: "" |
643 |
## stdout-json: "" |
644 |
|
645 |
#### declare -a arr does not remove existing arrays (OSH regression) |
646 |
case $SH in (dash) exit 99;; esac # dash does not support arrays |
647 |
|
648 |
declare -a arr |
649 |
arr=(foo bar baz) |
650 |
declare -a arr |
651 |
echo arr:${#arr[@]} |
652 |
## STDOUT: |
653 |
arr:3 |
654 |
## END |
655 |
## N-I dash status: 99 |
656 |
## N-I dash stdout-json: "" |
657 |
|
658 |
#### declare -A dict does not remove existing arrays (OSH regression) |
659 |
case $SH in (dash|mksh) exit 99;; esac # dash/mksh does not support associative arrays |
660 |
|
661 |
declare -A dict |
662 |
dict['foo']=hello |
663 |
dict['bar']=oil |
664 |
dict['baz']=world |
665 |
declare -A dict |
666 |
echo dict:${#dict[@]} |
667 |
## STDOUT: |
668 |
dict:3 |
669 |
## END |
670 |
## N-I dash/mksh status: 99 |
671 |
## N-I dash/mksh stdout-json: "" |
672 |
|