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: ""