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

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