OILS / spec / assign.test.sh View on Github | oils.pub

768 lines, 416 significant
1## oils_failures_allowed: 0
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 (eval redirects after evaluating arguments)
597
598# See also: spec/redir-order.test.sh (#2307)
599# The $(stdout_stderr.py) is evaluated *before* the 2>/dev/null redirection
600
601readonly x=$(stdout_stderr.py) 2>/dev/null
602echo done
603## STDOUT:
604done
605## END
606## STDERR:
607STDERR
608## END
609## BUG zsh stderr-json: ""
610
611#### redirect after command sub (like case above but without assignment builtin)
612echo stdout=$(stdout_stderr.py) 2>/dev/null
613## STDOUT:
614stdout=STDOUT
615## END
616## STDERR:
617STDERR
618## END
619
620#### redirect after bare assignment
621x=$(stdout_stderr.py) 2>/dev/null
622echo done
623## STDOUT:
624done
625## END
626## stderr-json: ""
627## BUG bash STDERR:
628STDERR
629## END
630
631#### redirect after declare -p
632case $SH in *dash) exit 99 ;; esac # stderr unpredictable
633
634foo=bar
635typeset -p foo 1>&2
636
637# zsh and mksh agree on exact output, which we don't really care about
638## STDERR:
639typeset foo=bar
640## END
641## OK bash STDERR:
642declare -- foo="bar"
643## END
644## OK osh STDERR:
645declare -- foo=bar
646## END
647## N-I dash status: 99
648## N-I dash stderr-json: ""
649## stdout-json: ""
650
651#### declare -a arr does not remove existing arrays (OSH regression)
652case $SH in dash) exit 99 ;; esac # dash does not support arrays
653
654declare -a arr
655arr=(foo bar baz)
656declare -a arr
657echo arr:${#arr[@]}
658## STDOUT:
659arr:3
660## END
661## N-I dash status: 99
662## N-I dash stdout-json: ""
663
664#### declare -A dict does not remove existing arrays (OSH regression)
665case $SH in dash|mksh) exit 99 ;; esac # dash/mksh does not support associative arrays
666
667declare -A dict
668dict['foo']=hello
669dict['bar']=oil
670dict['baz']=world
671declare -A dict
672echo dict:${#dict[@]}
673## STDOUT:
674dict:3
675## END
676## N-I dash/mksh status: 99
677## N-I dash/mksh stdout-json: ""
678
679#### "readonly -a arr" and "readonly -A dict" should not not remove existing arrays
680# mksh's readonly does not support the -a option.
681# dash/mksh does not support associative arrays.
682case $SH in dash|mksh) exit 99 ;; esac
683
684declare -a arr
685arr=(foo bar baz)
686declare -A dict
687dict['foo']=hello
688dict['bar']=oil
689dict['baz']=world
690
691readonly -a arr
692echo arr:${#arr[@]}
693readonly -A dict
694echo dict:${#dict[@]}
695## STDOUT:
696arr:3
697dict:3
698## END
699## N-I dash/mksh status: 99
700## N-I dash/mksh stdout-json: ""
701
702#### "declare -a arr" and "readonly -a a" creates an empty array (OSH)
703case $SH in dash|mksh) exit 99 ;; esac # dash/mksh does not support associative arrays
704
705declare -a arr1
706readonly -a arr2
707declare -A dict1
708readonly -A dict2
709
710declare -p arr1 arr2 dict1 dict2
711## STDOUT:
712declare -a arr1=()
713declare -ra arr2=()
714declare -A dict1=()
715declare -rA dict2=()
716## END
717## N-I bash STDOUT:
718declare -a arr1
719declare -r arr2
720declare -A dict1
721declare -r dict2
722## END
723## OK zsh STDOUT:
724typeset -a arr1
725arr1=( )
726typeset -a arr2
727arr2=( )
728typeset -ar arr2
729typeset -A dict1
730dict1=( )
731typeset -a dict2
732dict2=( )
733typeset -Ar dict2
734## END
735## N-I dash/mksh status: 99
736## N-I dash/mksh stdout-json: ""
737
738#### "var d = {}; declare -p d" does not print anything (OSH)
739case $SH in bash-4.4|dash|mksh|zsh) exit 99 ;; esac
740
741# We pretend that the variable does not exist when the variable is not
742# representable with the "declare -p" format.
743
744var d = {}
745declare -p d
746## STDOUT:
747## END
748## status: 1
749## N-I bash/dash/mksh/zsh status: 99
750
751#### readonly array should not be modified by a+=(1)
752case $SH in dash) exit 99 ;; esac # dash/mksh does not support associative arrays
753
754a=(1 2 3)
755readonly -a a
756eval 'a+=(4)'
757argv.py "${a[@]}"
758eval 'declare -n r=a; r+=(4)'
759argv.py "${a[@]}"
760
761## STDOUT:
762['1', '2', '3']
763['1', '2', '3']
764## END
765## OK mksh status: 1
766## OK mksh stdout-json: ""
767## N-I dash status: 99
768## N-I dash stdout-json: ""