1 ## oils_failures_allowed: 2
2 ## compare_shells: dash bash-4.4 mksh zsh
3
4 #### Env value doesn't persist
5 FOO=foo printenv.py FOO
6 echo -$FOO-
7 ## STDOUT:
8 foo
9 --
10 ## END
11
12 #### Env value with equals
13 FOO=foo=foo printenv.py FOO
14 ## stdout: foo=foo
15
16 #### Env binding can use preceding bindings, but not subsequent ones
17 # This means that for ASSIGNMENT_WORD, on the RHS you invoke the parser again!
18 # Could be any kind of quoted string.
19 FOO="foo" BAR="[$FOO][$BAZ]" BAZ=baz printenv.py FOO BAR BAZ
20 ## STDOUT:
21 foo
22 [foo][]
23 baz
24 ## BUG mksh STDOUT:
25 foo
26 [][]
27 baz
28 ## END
29
30 #### Env value with two quotes
31 FOO='foo'"adjacent" printenv.py FOO
32 ## stdout: fooadjacent
33
34 #### Env value with escaped <
35 FOO=foo\<foo printenv.py FOO
36 ## stdout: foo<foo
37
38 #### FOO=foo echo [foo]
39 FOO=foo echo "[$foo]"
40 ## stdout: []
41
42 #### FOO=foo fun
43 fun() {
44 echo "[$FOO]"
45 }
46 FOO=foo fun
47 ## stdout: [foo]
48
49 #### Multiple temporary envs on the stack
50 g() {
51 echo "$F" "$G1" "$G2"
52 echo '--- g() ---'
53 P=p printenv.py F G1 G2 A P
54 }
55 f() {
56 # NOTE: G1 doesn't pick up binding f, but G2 picks up a.
57 # I don't quite understand why this is, but bash and OSH agree!
58 G1=[$f] G2=[$a] g
59 echo '--- f() ---'
60 printenv.py F G1 G2 A P
61 }
62 a=A
63 F=f f
64 ## STDOUT:
65 f [] [A]
66 --- g() ---
67 f
68 []
69 [A]
70 None
71 p
72 --- f() ---
73 f
74 None
75 None
76 None
77 None
78 ## END
79 ## OK mksh STDOUT:
80 # G1 and G2 somehow persist. I think that is a bug. They should be local to
81 # the G call.
82 f [] [A]
83 --- g() ---
84 f
85 []
86 [A]
87 None
88 p
89 --- f() ---
90 f
91 []
92 [A]
93 None
94 None
95 ## END
96 ## BUG dash STDOUT:
97 # dash sets even less stuff. Doesn't appear correct.
98 f [] [A]
99 --- g() ---
100 None
101 None
102 None
103 None
104 p
105 --- f() ---
106 None
107 None
108 None
109 None
110 None
111 ## END
112
113 #### Escaped = in command name
114 # foo=bar is in the 'spec/bin' dir.
115 foo\=bar
116 ## stdout: HI
117
118 #### Env binding not allowed before compound command
119 # bash gives exit code 2 for syntax error, because of 'do'.
120 # dash gives 0 because there is stuff after for? Should really give an error.
121 # mksh gives acceptable error of 1.
122 FOO=bar for i in a b; do printenv.py $FOO; done
123 ## status: 2
124 ## OK mksh/zsh status: 1
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=mutated-temp
306 x=global
307 ## END
308 ## OK dash/zsh STDOUT:
309 x=temp-binding
310 x=mutated-temp
311 x=local
312 x=
313 x=global
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 ## OK dash/zsh STDOUT:
355 x=temp-binding
356 x=mutated-temp
357 x=
358 x=global
359 ## END
360 ## BUG mksh/yash STDOUT:
361 x=temp-binding
362 x=mutated-temp
363 x=
364 x=
365 ## END
366 ## 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 ## OK dash/bash/zsh STDOUT:
385 x=global
386 x=local
387 - operator = default
388 :- operator = default
389 ## END
390 ## 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 ## OK dash/zsh STDOUT:
409 x=temp-binding
410 x=local
411 - operator = default
412 :- operator = default
413 ## END
414 ## 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.py "$foo"
510 unset foo
511
512 export foo=*
513 argv.py "$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.py "$foo"
530 unset foo
531 ## STDOUT:
532 ['*']
533 ## END
534 ## N-I dash STDOUT:
535 ['']
536 ## END
537
538 #### unset and shell funcs
539 # See also ysh-proc > 'unset and procs'
540
541 foo() {
542 echo bar
543 }
544
545 foo
546
547 declare -F
548 unset foo
549 declare -F
550
551 foo
552
553 ## status: 127
554 ## STDOUT:
555 bar
556 declare -f foo
557 ## END
558
559 #### readonly $x where x='b c'
560 one=a
561 two='b c'
562 readonly $two $one
563 a=new
564 echo status=$?
565 b=new
566 echo status=$?
567 c=new
568 echo status=$?
569
570 # in OSH and zsh, this is an invalid variable name
571 ## status: 1
572 ## stdout-json: ""
573
574 # most shells make two variable read-only
575
576 ## OK dash/mksh status: 2
577 ## OK bash status: 0
578 ## OK bash STDOUT:
579 status=1
580 status=1
581 status=1
582 ## END
583
584 #### readonly a=(1 2) no_value c=(3 4) makes 'no_value' readonly
585 readonly a=(1 2) no_value c=(3 4)
586 no_value=x
587 ## status: 1
588 ## stdout-json: ""
589 ## OK dash status: 2
590
591 #### export a=1 no_value c=2
592 no_value=foo
593 export a=1 no_value c=2
594 printenv.py no_value
595 ## STDOUT:
596 foo
597 ## END
598
599 #### local a=loc $var c=loc
600 var='b'
601 b=global
602 echo $b
603 f() {
604 local a=loc $var c=loc
605 argv.py "$a" "$b" "$c"
606 }
607 f
608 ## STDOUT:
609 global
610 ['loc', '', 'loc']
611 ## END
612 ## BUG dash STDOUT:
613 global
614 ['loc', 'global', 'loc']
615 ## END
616
617 #### redirect after assignment builtin (what's going on with dash/bash/mksh here?)
618 readonly x=$(stdout_stderr.py) 2>/dev/null
619 echo done
620 ## STDOUT:
621 done
622 ## END
623 ## STDERR:
624 STDERR
625 ## END
626 ## BUG zsh stderr-json: ""
627
628 #### redirect after command sub (like case above but without assignment builtin)
629 echo stdout=$(stdout_stderr.py) 2>/dev/null
630 ## STDOUT:
631 stdout=STDOUT
632 ## END
633 ## STDERR:
634 STDERR
635 ## END
636
637 #### redirect after bare assignment
638 x=$(stdout_stderr.py) 2>/dev/null
639 echo done
640 ## STDOUT:
641 done
642 ## END
643 ## stderr-json: ""
644 ## BUG bash STDERR:
645 STDERR
646 ## END
647
648 #### redirect after declare -p
649 case $SH in *dash) exit 99 ;; esac # stderr unpredictable
650
651 foo=bar
652 typeset -p foo 1>&2
653
654 # zsh and mksh agree on exact output, which we don't really care about
655 ## STDERR:
656 typeset foo=bar
657 ## END
658 ## OK bash STDERR:
659 declare -- foo="bar"
660 ## END
661 ## OK osh STDERR:
662 declare -- foo=bar
663 ## END
664 ## N-I dash status: 99
665 ## N-I dash stderr-json: ""
666 ## stdout-json: ""
667
668 #### declare -a arr does not remove existing arrays (OSH regression)
669 case $SH in (dash) exit 99;; esac # dash does not support arrays
670
671 declare -a arr
672 arr=(foo bar baz)
673 declare -a arr
674 echo arr:${#arr[@]}
675 ## STDOUT:
676 arr:3
677 ## END
678 ## N-I dash status: 99
679 ## N-I dash stdout-json: ""
680
681 #### declare -A dict does not remove existing arrays (OSH regression)
682 case $SH in (dash|mksh) exit 99;; esac # dash/mksh does not support associative arrays
683
684 declare -A dict
685 dict['foo']=hello
686 dict['bar']=oil
687 dict['baz']=world
688 declare -A dict
689 echo dict:${#dict[@]}
690 ## STDOUT:
691 dict:3
692 ## END
693 ## N-I dash/mksh status: 99
694 ## N-I dash/mksh stdout-json: ""
695