OILS / spec / assign.test.sh View on Github | oilshell.org

674 lines, 367 significant
1## oils_failures_allowed: 2
2## compare_shells: dash bash-4.4 mksh zsh
3
4#### Env value doesn't persist
5FOO=foo printenv.py FOO
6echo -$FOO-
7## STDOUT:
8foo
9--
10## END
11
12#### Env value with equals
13FOO=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.
19FOO="foo" BAR="[$FOO][$BAZ]" BAZ=baz printenv.py FOO BAR BAZ
20## STDOUT:
21foo
22[foo][]
23baz
24## BUG mksh STDOUT:
25foo
26[][]
27baz
28## END
29
30#### Env value with two quotes
31FOO='foo'"adjacent" printenv.py FOO
32## stdout: fooadjacent
33
34#### Env value with escaped <
35FOO=foo\<foo printenv.py FOO
36## stdout: foo<foo
37
38#### FOO=foo echo [foo]
39FOO=foo echo "[$foo]"
40## stdout: []
41
42#### FOO=foo fun
43fun() {
44 echo "[$FOO]"
45}
46FOO=foo fun
47## stdout: [foo]
48
49#### Multiple temporary envs on the stack
50g() {
51 echo "$F" "$G1" "$G2"
52 echo '--- g() ---'
53 P=p printenv.py F G1 G2 A P
54}
55f() {
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}
62a=A
63F=f f
64## STDOUT:
65f [] [A]
66--- g() ---
67f
68[]
69[A]
70None
71p
72--- f() ---
73f
74None
75None
76None
77None
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.
82f [] [A]
83--- g() ---
84f
85[]
86[A]
87None
88p
89--- f() ---
90f
91[]
92[A]
93None
94None
95## END
96## BUG dash STDOUT:
97# dash sets even less stuff. Doesn't appear correct.
98f [] [A]
99--- g() ---
100None
101None
102None
103None
104p
105--- f() ---
106None
107None
108None
109None
110None
111## END
112
113#### Escaped = in command name
114# foo=bar is in the 'spec/bin' dir.
115foo\=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.
122FOO=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'
127FOO=bar for
128## status: 127
129## OK zsh status: 1
130
131#### Empty env binding
132EMPTY= printenv.py EMPTY
133## stdout:
134
135#### Assignment doesn't do word splitting
136words='one two'
137a=$words
138argv.py "$a"
139## stdout: ['one two']
140
141#### Assignment doesn't do glob expansion
142touch _tmp/z.Z _tmp/zz.Z
143a=_tmp/*.Z
144argv.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.
150FOO=foo readonly v=$(printenv.py FOO)
151echo "v=$v"
152
153# bash has probems here:
154FOO=foo readonly v2=$FOO
155echo "v2=$v2"
156
157## STDOUT:
158v=None
159v2=foo
160## END
161## BUG bash STDOUT:
162v=None
163v2=
164## END
165
166#### assignments / array assignments not interpreted after 'echo'
167a=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)
174f() {
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}
183f 'x=y a=b'
184## OK dash/bash/mksh STDOUT:
185x='y a=b'
186a=''
187x='y'
188a='b'
189## END
190# osh and zsh don't do word splitting
191## STDOUT:
192x='y a=b'
193a=''
194x='y a=b'
195a=''
196## END
197
198#### readonly x= gives empty string (regression)
199readonly x=
200argv.py "$x"
201## STDOUT:
202['']
203## END
204
205#### 'local x' does not set variable
206set -o nounset
207f() {
208 local x
209 echo $x
210}
211f
212## status: 1
213## OK dash status: 2
214## BUG zsh status: 0
215
216#### 'local -a x' does not set variable
217set -o nounset
218f() {
219 local -a x
220 echo $x
221}
222f
223## status: 1
224## OK dash status: 2
225## BUG zsh status: 0
226
227#### 'local x' and then array assignment
228f() {
229 local x
230 x[3]=foo
231 echo ${x[3]}
232}
233f
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
241declare -A foo
242key=bar
243foo["$key"]=value
244echo ${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
254if ! foo=bar; then
255 echo BAD
256fi
257echo $foo
258if ! eval 'spam=eggs'; then
259 echo BAD
260fi
261echo $spam
262## STDOUT:
263bar
264eggs
265## END
266
267
268#### Modify a temporary binding
269# (regression for bug found by Michael Greenberg)
270f() {
271 echo "x before = $x"
272 x=$((x+1))
273 echo "x after = $x"
274}
275x=5 f
276## STDOUT:
277x before = 5
278x after = 6
279## END
280
281#### Reveal existence of "temp frame" (All shells disagree here!!!)
282f() {
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
297x=global
298x=temp-binding f
299echo "x=$x"
300
301## STDOUT:
302x=temp-binding
303x=mutated-temp
304x=local
305x=mutated-temp
306x=global
307## END
308## OK dash/zsh STDOUT:
309x=temp-binding
310x=mutated-temp
311x=local
312x=
313x=global
314## END
315## BUG bash STDOUT:
316x=temp-binding
317x=mutated-temp
318x=local
319x=global
320x=global
321## END
322## BUG mksh STDOUT:
323x=temp-binding
324x=mutated-temp
325x=local
326x=mutated-temp
327x=mutated-temp
328## END
329## BUG yash STDOUT:
330# yash has no locals
331x=temp-binding
332x=mutated-temp
333x=mutated-temp
334x=
335x=
336## END
337
338#### Test above without 'local' (which is not POSIX)
339f() {
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
350x=global
351x=temp-binding f
352echo "x=$x"
353
354## OK dash/zsh STDOUT:
355x=temp-binding
356x=mutated-temp
357x=
358x=global
359## END
360## BUG mksh/yash STDOUT:
361x=temp-binding
362x=mutated-temp
363x=
364x=
365## END
366## STDOUT:
367x=temp-binding
368x=mutated-temp
369x=global
370x=global
371## END
372
373#### Using ${x-default} after unsetting local shadowing a global
374f() {
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}
382x=global
383f
384## OK dash/bash/zsh STDOUT:
385x=global
386x=local
387- operator = default
388:- operator = default
389## END
390## STDOUT:
391x=global
392x=local
393- operator = global
394:- operator = global
395## END
396
397#### Using ${x-default} after unsetting a temp binding shadowing a global
398f() {
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}
406x=global
407x=temp-binding f
408## OK dash/zsh STDOUT:
409x=temp-binding
410x=local
411- operator = default
412:- operator = default
413## END
414## STDOUT:
415x=temp-binding
416x=local
417- operator = temp-binding
418:- operator = temp-binding
419## END
420## BUG bash STDOUT:
421x=temp-binding
422x=local
423- operator = global
424:- operator = global
425## END
426
427#### static assignment doesn't split
428words='a b c'
429export ex=$words
430glo=$words
431readonly ro=$words
432argv.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
443shopt -s expand_aliases || true
444words='a b c'
445alias e=export
446alias r=readonly
447e ex=$words
448r ro=$words
449argv.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)
459words='a b c'
460e=export
461r=readonly
462$e ex=$words
463$r ro=$words
464argv.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
477words='a b c'
478arg_ex=ex=$words
479arg_ro=ro=$words
480
481# no quotes, this is split of course
482export $arg_ex
483readonly $arg_ro
484
485argv.py "$ex" "$ro"
486
487arg_ex2=ex2=$words
488arg_ro2=ro2=$words
489
490# quotes, no splitting
491export "$arg_ex2"
492readonly "$arg_ro2"
493
494argv.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
506cd $TMP
507touch foo=a foo=b
508foo=*
509argv.py "$foo"
510unset foo
511
512export foo=*
513argv.py "$foo"
514unset foo
515
516## STDOUT:
517['*']
518['*']
519## END
520## BUG dash STDOUT:
521['*']
522['b']
523## END
524
525#### declare and glob
526cd $TMP
527touch foo=a foo=b
528typeset foo=*
529argv.py "$foo"
530unset foo
531## STDOUT:
532['*']
533## END
534## N-I dash STDOUT:
535['']
536## END
537
538#### readonly $x where x='b c'
539one=a
540two='b c'
541readonly $two $one
542a=new
543echo status=$?
544b=new
545echo status=$?
546c=new
547echo 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:
558status=1
559status=1
560status=1
561## END
562
563#### readonly a=(1 2) no_value c=(3 4) makes 'no_value' readonly
564readonly a=(1 2) no_value c=(3 4)
565no_value=x
566## status: 1
567## stdout-json: ""
568## OK dash status: 2
569
570#### export a=1 no_value c=2
571no_value=foo
572export a=1 no_value c=2
573printenv.py no_value
574## STDOUT:
575foo
576## END
577
578#### local a=loc $var c=loc
579var='b'
580b=global
581echo $b
582f() {
583 local a=loc $var c=loc
584 argv.py "$a" "$b" "$c"
585}
586f
587## STDOUT:
588global
589['loc', '', 'loc']
590## END
591## BUG dash STDOUT:
592global
593['loc', 'global', 'loc']
594## END
595
596#### redirect after assignment builtin (what's going on with dash/bash/mksh here?)
597readonly x=$(stdout_stderr.py) 2>/dev/null
598echo done
599## STDOUT:
600done
601## END
602## STDERR:
603STDERR
604## END
605## BUG zsh stderr-json: ""
606
607#### redirect after command sub (like case above but without assignment builtin)
608echo stdout=$(stdout_stderr.py) 2>/dev/null
609## STDOUT:
610stdout=STDOUT
611## END
612## STDERR:
613STDERR
614## END
615
616#### redirect after bare assignment
617x=$(stdout_stderr.py) 2>/dev/null
618echo done
619## STDOUT:
620done
621## END
622## stderr-json: ""
623## BUG bash STDERR:
624STDERR
625## END
626
627#### redirect after declare -p
628case $SH in *dash) exit 99 ;; esac # stderr unpredictable
629
630foo=bar
631typeset -p foo 1>&2
632
633# zsh and mksh agree on exact output, which we don't really care about
634## STDERR:
635typeset foo=bar
636## END
637## OK bash STDERR:
638declare -- foo="bar"
639## END
640## OK osh STDERR:
641declare -- foo=bar
642## END
643## N-I dash status: 99
644## N-I dash stderr-json: ""
645## stdout-json: ""
646
647#### declare -a arr does not remove existing arrays (OSH regression)
648case $SH in (dash) exit 99;; esac # dash does not support arrays
649
650declare -a arr
651arr=(foo bar baz)
652declare -a arr
653echo arr:${#arr[@]}
654## STDOUT:
655arr:3
656## END
657## N-I dash status: 99
658## N-I dash stdout-json: ""
659
660#### declare -A dict does not remove existing arrays (OSH regression)
661case $SH in (dash|mksh) exit 99;; esac # dash/mksh does not support associative arrays
662
663declare -A dict
664dict['foo']=hello
665dict['bar']=oil
666dict['baz']=world
667declare -A dict
668echo dict:${#dict[@]}
669## STDOUT:
670dict:3
671## END
672## N-I dash/mksh status: 99
673## N-I dash/mksh stdout-json: ""
674