OILS / spec / var-op-test.test.sh View on Github | oils.pub

829 lines, 442 significant
1## compare_shells: bash dash mksh zsh
2## oils_failures_allowed: 0
3
4#### Lazy Evaluation of Alternative
5i=0
6x=x
7echo ${x:-$((i++))}
8echo $i
9echo ${undefined:-$((i++))}
10echo $i # i is one because the alternative was only evaluated once
11## status: 0
12## STDOUT:
13x
140
150
161
17## END
18## N-I dash status: 2
19## N-I dash STDOUT:
20x
210
22## END
23
24#### Default value when empty
25empty=''
26echo ${empty:-is empty}
27## stdout: is empty
28
29#### Default value when unset
30echo ${unset-is unset}
31## stdout: is unset
32
33#### Unquoted with array as default value
34set -- '1 2' '3 4'
35argv.py X${unset=x"$@"x}X
36argv.py X${unset=x$@x}X # If you want OSH to split, write this
37# osh
38## STDOUT:
39['Xx1', '2', '3', '4xX']
40['Xx1', '2', '3', '4xX']
41## END
42## OK osh STDOUT:
43['Xx1 2', '3 4xX']
44['Xx1', '2', '3', '4xX']
45## END
46## OK zsh STDOUT:
47['Xx1 2 3 4xX']
48['Xx1 2 3 4xX']
49## END
50
51#### Quoted with array as default value
52set -- '1 2' '3 4'
53argv.py "X${unset=x"$@"x}X"
54argv.py "X${unset=x$@x}X" # OSH is the same here
55## STDOUT:
56['Xx1 2 3 4xX']
57['Xx1 2 3 4xX']
58## END
59
60# Bash 4.2..4.4 had a bug. This was fixed in Bash 5.0.
61#
62# ## BUG bash STDOUT:
63# ['Xx1', '2', '3', '4xX']
64# ['Xx1 2 3 4xX']
65# ## END
66
67## OK osh STDOUT:
68['Xx1 2', '3 4xX']
69['Xx1 2 3 4xX']
70## END
71
72#### Assign default with array
73set -- '1 2' '3 4'
74argv.py X${unset=x"$@"x}X
75argv.py "$unset"
76## STDOUT:
77['Xx1', '2', '3', '4xX']
78['x1 2 3 4x']
79## END
80## OK osh STDOUT:
81['Xx1 2', '3 4xX']
82['x1 2 3 4x']
83## END
84## OK zsh STDOUT:
85['Xx1 2 3 4xX']
86['x1 2 3 4x']
87## END
88
89#### Assign default value when empty
90empty=''
91${empty:=is empty}
92echo $empty
93## stdout: is empty
94
95#### Assign default value when unset
96${unset=is unset}
97echo $unset
98## stdout: is unset
99
100#### ${v:+foo} Alternative value when empty
101v=foo
102empty=''
103echo ${v:+v is not empty} ${empty:+is not empty}
104## stdout: v is not empty
105
106#### ${v+foo} Alternative value when unset
107v=foo
108echo ${v+v is not unset} ${unset:+is not unset}
109## stdout: v is not unset
110
111#### "${x+foo}" quoted (regression)
112# Python's configure caught this
113argv.py "${with_icc+set}" = set
114## STDOUT:
115['', '=', 'set']
116## END
117
118#### ${s+foo} and ${s:+foo} when set -u
119set -u
120v=v
121echo v=${v:+foo}
122echo v=${v+foo}
123unset v
124echo v=${v:+foo}
125echo v=${v+foo}
126## STDOUT:
127v=foo
128v=foo
129v=
130v=
131## END
132
133#### "${array[@]} with set -u (bash is outlier)
134case $SH in dash) exit ;; esac
135
136set -u
137
138typeset -a empty
139empty=()
140
141echo empty /"${empty[@]}"/
142echo undefined /"${undefined[@]}"/
143
144## status: 1
145## STDOUT:
146empty //
147## END
148
149## BUG bash status: 0
150## BUG bash STDOUT:
151empty //
152undefined //
153## END
154
155# empty array is unset in mksh
156## BUG mksh status: 1
157## BUG mksh STDOUT:
158## END
159
160## N-I dash status: 0
161## N-I dash STDOUT:
162## END
163
164
165#### "${undefined[@]+foo}" and "${undefined[@]:+foo}", with set -u
166case $SH in dash) exit ;; esac
167
168set -u
169
170echo plus /"${array[@]+foo}"/
171echo plus colon /"${array[@]:+foo}"/
172
173## STDOUT:
174plus //
175plus colon //
176## END
177
178## N-I dash STDOUT:
179## END
180
181#### "${a[@]+foo}" and "${a[@]:+foo}" - operators are equivalent on arrays?
182
183case $SH in dash) exit ;; esac
184
185echo '+ ' /"${array[@]+foo}"/
186echo '+:' /"${array[@]:+foo}"/
187echo
188
189typeset -a array
190array=()
191
192echo '+ ' /"${array[@]+foo}"/
193echo '+:' /"${array[@]:+foo}"/
194echo
195
196array=('')
197
198echo '+ ' /"${array[@]+foo}"/
199echo '+:' /"${array[@]:+foo}"/
200echo
201
202array=(spam eggs)
203
204echo '+ ' /"${array[@]+foo}"/
205echo '+:' /"${array[@]:+foo}"/
206echo
207
208
209## BUG mksh STDOUT:
210+ //
211+: //
212
213+ //
214+: //
215
216+ /foo/
217+: //
218
219+ /foo/
220+: /foo/
221
222## END
223
224# Bash 2.0..4.4 has a bug that "${a[@]:-xxx}" produces an empty string. It
225# seemed to consider a[@] and a[*] are non-empty when there is at least one
226# element even if the element is empty. This was fixed in Bash 5.0.
227#
228# ## BUG bash STDOUT:
229# + //
230# +: //
231#
232# + //
233# +: //
234#
235# + /foo/
236# +: /foo/
237#
238# + /foo/
239# +: /foo/
240#
241# ## END
242
243## BUG zsh STDOUT:
244+ //
245+: //
246
247+ /foo/
248+: //
249
250+ /foo/
251+: /foo/
252
253+ /foo/
254+: /foo/
255
256## END
257
258## N-I dash STDOUT:
259## END
260
261
262
263#### Nix idiom ${!hooksSlice+"${!hooksSlice}"} - was workaround for obsolete bash 4.3 bug
264
265case $SH in dash|mksh|zsh) exit ;; esac
266
267# https://oilshell.zulipchat.com/#narrow/stream/307442-nix/topic/Replacing.20bash.20with.20osh.20in.20Nixpkgs.20stdenv
268
269(argv.py ${!hooksSlice+"${!hooksSlice}"})
270
271hooksSlice=x
272
273argv.py ${!hooksSlice+"${!hooksSlice}"}
274
275declare -a hookSlice=()
276
277argv.py ${!hooksSlice+"${!hooksSlice}"}
278
279foo=42
280bar=43
281
282declare -a hooksSlice=(foo bar spam eggs)
283
284argv.py ${!hooksSlice+"${!hooksSlice}"}
285
286## STDOUT:
287[]
288[]
289['42']
290## END
291
292# Bash 4.4 has a bug that ${!undef-} successfully generates an empty word.
293#
294# ## BUG bash STDOUT:
295# []
296# []
297# []
298# ['42']
299# ## END
300
301## OK dash/mksh/zsh STDOUT:
302## END
303
304#### ${v-foo} and ${v:-foo} when set -u
305set -u
306v=v
307echo v=${v:-foo}
308echo v=${v-foo}
309unset v
310echo v=${v:-foo}
311echo v=${v-foo}
312## STDOUT:
313v=v
314v=v
315v=foo
316v=foo
317## END
318
319#### array and - and +
320case $SH in (dash) exit ;; esac
321
322shopt -s compat_array # to refer to array as scalar
323
324empty=()
325a1=('')
326a2=('' x)
327a3=(3 4)
328echo empty=${empty[@]-minus}
329echo a1=${a1[@]-minus}
330echo a1[0]=${a1[0]-minus}
331echo a2=${a2[@]-minus}
332echo a3=${a3[@]-minus}
333echo ---
334
335echo empty=${empty[@]+plus}
336echo a1=${a1[@]+plus}
337echo a1[0]=${a1[0]+plus}
338echo a2=${a2[@]+plus}
339echo a3=${a3[@]+plus}
340echo ---
341
342echo empty=${empty+plus}
343echo a1=${a1+plus}
344echo a2=${a2+plus}
345echo a3=${a3+plus}
346echo ---
347
348# Test quoted arrays too
349argv.py "${empty[@]-minus}"
350argv.py "${empty[@]+plus}"
351argv.py "${a1[@]-minus}"
352argv.py "${a1[@]+plus}"
353argv.py "${a1[0]-minus}"
354argv.py "${a1[0]+plus}"
355argv.py "${a2[@]-minus}"
356argv.py "${a2[@]+plus}"
357argv.py "${a3[@]-minus}"
358argv.py "${a3[@]+plus}"
359
360## STDOUT:
361empty=minus
362a1=
363a1[0]=
364a2= x
365a3=3 4
366---
367empty=
368a1=plus
369a1[0]=plus
370a2=plus
371a3=plus
372---
373empty=
374a1=plus
375a2=plus
376a3=plus
377---
378['minus']
379[]
380['']
381['plus']
382['']
383['plus']
384['', 'x']
385['plus']
386['3', '4']
387['plus']
388## END
389## N-I dash stdout-json: ""
390## N-I zsh STDOUT:
391empty=
392a1=
393## END
394## N-I zsh status: 1
395
396#### $@ (empty) and - and +
397echo argv=${@-minus}
398echo argv=${@+plus}
399echo argv=${@:-minus}
400echo argv=${@:+plus}
401## STDOUT:
402argv=minus
403argv=
404argv=minus
405argv=
406## END
407## BUG dash/zsh STDOUT:
408argv=
409argv=plus
410argv=minus
411argv=
412## END
413
414#### $@ ("") and - and +
415set -- ""
416echo argv=${@-minus}
417echo argv=${@+plus}
418echo argv=${@:-minus}
419echo argv=${@:+plus}
420## STDOUT:
421argv=
422argv=plus
423argv=minus
424argv=
425## END
426
427# Zsh treats $@ as an array unlike Bash converting it to a string by joining it
428# with a space.
429
430## OK zsh STDOUT:
431argv=
432argv=plus
433argv=
434argv=plus
435## END
436
437#### $@ ("" "") and - and +
438set -- "" ""
439echo argv=${@-minus}
440echo argv=${@+plus}
441echo argv=${@:-minus}
442echo argv=${@:+plus}
443## STDOUT:
444argv=
445argv=plus
446argv=
447argv=plus
448## END
449
450#### $* ("" "") and - and + (IFS=)
451set -- "" ""
452IFS=
453echo argv=${*-minus}
454echo argv=${*+plus}
455echo argv=${*:-minus}
456echo argv=${*:+plus}
457## STDOUT:
458argv=
459argv=plus
460argv=
461argv=plus
462## END
463## BUG mksh STDOUT:
464argv=
465argv=plus
466argv=minus
467argv=
468## END
469
470#### "$*" ("" "") and - and + (IFS=)
471set -- "" ""
472IFS=
473echo "argv=${*-minus}"
474echo "argv=${*+plus}"
475echo "argv=${*:-minus}"
476echo "argv=${*:+plus}"
477## STDOUT:
478argv=
479argv=plus
480argv=minus
481argv=
482## END
483
484#### assoc array and - and +
485case $SH in (dash|mksh) exit ;; esac
486
487declare -A empty=()
488declare -A assoc=(['k']=v)
489
490echo empty=${empty[@]-minus}
491echo empty=${empty[@]+plus}
492echo assoc=${assoc[@]-minus}
493echo assoc=${assoc[@]+plus}
494
495echo ---
496echo empty=${empty[@]:-minus}
497echo empty=${empty[@]:+plus}
498echo assoc=${assoc[@]:-minus}
499echo assoc=${assoc[@]:+plus}
500## STDOUT:
501empty=minus
502empty=
503assoc=v
504assoc=plus
505---
506empty=minus
507empty=
508assoc=v
509assoc=plus
510## END
511
512## BUG zsh STDOUT:
513empty=
514empty=plus
515assoc=minus
516assoc=
517---
518empty=minus
519empty=
520assoc=minus
521assoc=
522## END
523
524## N-I dash/mksh STDOUT:
525## END
526
527
528#### Error when empty
529empty=''
530echo ${empty:?'is em'pty} # test eval of error
531echo should not get here
532## stdout-json: ""
533## status: 1
534## OK dash status: 2
535
536#### Error when unset
537echo ${unset?is empty}
538echo should not get here
539## stdout-json: ""
540## status: 1
541## OK dash status: 2
542
543#### Error when unset
544v=foo
545echo ${v+v is not unset} ${unset:+is not unset}
546## stdout: v is not unset
547
548#### ${var=x} dynamic scope
549f() { : "${hello:=x}"; echo $hello; }
550f
551echo hello=$hello
552
553f() { hello=x; }
554f
555echo hello=$hello
556## STDOUT:
557x
558hello=x
559hello=x
560## END
561
562#### array ${arr[0]=x}
563arr=()
564echo ${#arr[@]}
565: ${arr[0]=x}
566echo ${#arr[@]}
567## STDOUT:
5680
5691
570## END
571## N-I dash status: 2
572## N-I dash stdout-json: ""
573## N-I zsh status: 1
574## N-I zsh STDOUT:
5750
576## END
577
578#### assoc array ${arr["k"]=x}
579# note: this also works in zsh
580
581declare -A arr=()
582echo ${#arr[@]}
583: ${arr['k']=x}
584echo ${#arr[@]}
585## STDOUT:
5860
5871
588## END
589## N-I dash status: 2
590## N-I dash stdout-json: ""
591## N-I mksh status: 1
592## N-I mksh stdout-json: ""
593
594#### "\z" as arg
595echo "${undef-\$}"
596echo "${undef-\(}"
597echo "${undef-\z}"
598echo "${undef-\"}"
599echo "${undef-\`}"
600echo "${undef-\\}"
601## STDOUT:
602$
603\(
604\z
605"
606`
607\
608## END
609## BUG yash STDOUT:
610$
611(
612z
613"
614`
615\
616## END
617# Note: this line terminates the quoting by ` not to confuse the text editor.
618
619
620#### "\e" as arg
621echo "${undef-\e}"
622## STDOUT:
623\e
624## END
625## BUG zsh/mksh stdout-repr: '\x1b\n'
626## BUG yash stdout: e
627
628
629#### op-test for ${a} and ${a[0]}
630case $SH in dash) exit ;; esac
631
632test-hyphen() {
633 echo "a : '${a-no-colon}' '${a:-with-colon}'"
634 echo "a[0]: '${a[0]-no-colon}' '${a[0]:-with-colon}'"
635}
636
637a=()
638test-hyphen
639a=("")
640test-hyphen
641a=("" "")
642test-hyphen
643IFS=
644test-hyphen
645
646## STDOUT:
647a : 'no-colon' 'with-colon'
648a[0]: 'no-colon' 'with-colon'
649a : '' 'with-colon'
650a[0]: '' 'with-colon'
651a : '' 'with-colon'
652a[0]: '' 'with-colon'
653a : '' 'with-colon'
654a[0]: '' 'with-colon'
655## END
656
657# Zsh's ${a} and ${a[@]} implement something different from the other shells'.
658
659## OK zsh STDOUT:
660a : '' 'with-colon'
661a[0]: 'no-colon' 'with-colon'
662a : '' 'with-colon'
663a[0]: 'no-colon' 'with-colon'
664a : ' ' ' '
665a[0]: 'no-colon' 'with-colon'
666a : '' 'with-colon'
667a[0]: 'no-colon' 'with-colon'
668## END
669
670## N-I dash STDOUT:
671## END:
672
673
674#### op-test for ${a[@]} and ${a[*]}
675case $SH in dash) exit ;; esac
676
677test-hyphen() {
678 echo "a[@]: '${a[@]-no-colon}' '${a[@]:-with-colon}'"
679 echo "a[*]: '${a[*]-no-colon}' '${a[*]:-with-colon}'"
680}
681
682a=()
683test-hyphen
684a=("")
685test-hyphen
686a=("" "")
687test-hyphen
688IFS=
689test-hyphen
690
691## STDOUT:
692a[@]: 'no-colon' 'with-colon'
693a[*]: 'no-colon' 'with-colon'
694a[@]: '' 'with-colon'
695a[*]: '' 'with-colon'
696a[@]: ' ' ' '
697a[*]: ' ' ' '
698a[@]: ' ' ' '
699a[*]: '' 'with-colon'
700## END
701
702# Bash 2.0..4.4 has a bug that "${a[@]:-xxx}" produces an empty string. It
703# seemed to consider a[@] and a[*] are non-empty when there is at least one
704# element even if the element is empty. This was fixed in Bash 5.0.
705#
706# ## BUG bash STDOUT:
707# a[@]: 'no-colon' 'with-colon'
708# a[*]: 'no-colon' 'with-colon'
709# a[@]: '' ''
710# a[*]: '' ''
711# a[@]: ' ' ' '
712# a[*]: ' ' ' '
713# a[@]: ' ' ' '
714# a[*]: '' ''
715# ## END
716
717# Zsh's ${a} and ${a[@]} implement something different from the other shells'.
718
719## OK zsh STDOUT:
720a[@]: '' 'with-colon'
721a[*]: '' 'with-colon'
722a[@]: '' ''
723a[*]: '' 'with-colon'
724a[@]: ' ' ' '
725a[*]: ' ' ' '
726a[@]: ' ' ' '
727a[*]: '' 'with-colon'
728## END
729
730## N-I dash STDOUT:
731## END:
732
733
734#### op-test for ${!array} with array="a" and array="a[0]"
735case $SH in dash|mksh|zsh) exit ;; esac
736
737test-hyphen() {
738 ref='a'
739 echo "ref=a : '${!ref-no-colon}' '${!ref:-with-colon}'"
740 ref='a[0]'
741 echo "ref=a[0]: '${!ref-no-colon}' '${!ref:-with-colon}'"
742}
743
744a=()
745test-hyphen
746a=("")
747test-hyphen
748a=("" "")
749test-hyphen
750IFS=
751test-hyphen
752
753## STDOUT:
754ref=a : 'no-colon' 'with-colon'
755ref=a[0]: 'no-colon' 'with-colon'
756ref=a : '' 'with-colon'
757ref=a[0]: '' 'with-colon'
758ref=a : '' 'with-colon'
759ref=a[0]: '' 'with-colon'
760ref=a : '' 'with-colon'
761ref=a[0]: '' 'with-colon'
762## END
763
764## N-I dash/mksh/zsh STDOUT:
765## END:
766
767
768#### op-test for ${!array} with array="a[@]" or array="a[*]"
769case $SH in dash|mksh|zsh) exit ;; esac
770
771test-hyphen() {
772 ref='a[@]'
773 echo "ref=a[@]: '${!ref-no-colon}' '${!ref:-with-colon}'"
774 ref='a[*]'
775 echo "ref=a[*]: '${!ref-no-colon}' '${!ref:-with-colon}'"
776}
777
778a=()
779test-hyphen
780a=("")
781test-hyphen
782a=("" "")
783test-hyphen
784IFS=
785test-hyphen
786
787## STDOUT:
788ref=a[@]: 'no-colon' 'with-colon'
789ref=a[*]: 'no-colon' 'with-colon'
790ref=a[@]: '' 'with-colon'
791ref=a[*]: '' 'with-colon'
792ref=a[@]: ' ' ' '
793ref=a[*]: ' ' ' '
794ref=a[@]: ' ' ' '
795ref=a[*]: '' 'with-colon'
796## END
797
798## BUG bash STDOUT:
799ref=a[@]: 'no-colon' 'with-colon'
800ref=a[*]: 'no-colon' 'with-colon'
801ref=a[@]: '' ''
802ref=a[*]: '' ''
803ref=a[@]: ' ' ' '
804ref=a[*]: ' ' ' '
805ref=a[@]: ' ' ' '
806ref=a[*]: '' ''
807## END
808
809## N-I dash/mksh/zsh STDOUT:
810## END:
811
812
813#### op-test for unquoted ${a[*]:-empty} with IFS=
814case $SH in dash) exit ;; esac
815
816IFS=
817a=("" "")
818argv.py ${a[*]:-empty}
819
820## STDOUT:
821[]
822## END
823
824## BUG mksh STDOUT:
825['empty']
826## END
827
828## N-I dash STDOUT:
829## END: