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

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