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

765 lines, 424 significant
1## compare_shells: bash dash mksh
2## oils_failures_allowed: 1
3## tags: interactive
4
5# Test options to set, shopt, $SH.
6
7#### $- with -c
8# dash's behavior seems most sensible here?
9$SH -o nounset -c 'echo $-'
10## stdout: u
11## OK bash stdout: huBc
12## OK mksh stdout: uhc
13## status: 0
14
15#### $- with pipefail
16set -o pipefail -o nounset
17echo $-
18## stdout: u
19## status: 0
20## OK bash stdout: huBs
21## OK mksh stdout: ush
22## N-I dash stdout-json: ""
23## N-I dash status: 2
24
25#### $- and more options
26set -efuC
27o=$-
28[[ $o == *e* ]]; echo yes
29[[ $o == *f* ]]; echo yes
30[[ $o == *u* ]]; echo yes
31[[ $o == *C* ]]; echo yes
32## STDOUT:
33yes
34yes
35yes
36yes
37## END
38## N-I dash stdout-json: ""
39## N-I dash status: 127
40
41#### $- with interactive shell
42$SH -c 'echo $-' | grep i || echo FALSE
43$SH -i -c 'echo $-' | grep -q i && echo TRUE
44## STDOUT:
45FALSE
46TRUE
47## END
48#### pass short options like sh -e
49$SH -e -c 'false; echo status=$?'
50## stdout-json: ""
51## status: 1
52
53#### pass long options like sh -o errexit
54$SH -o errexit -c 'false; echo status=$?'
55## stdout-json: ""
56## status: 1
57
58#### pass shopt options like sh -O nullglob
59$SH +O nullglob -c 'echo foo *.nonexistent bar'
60$SH -O nullglob -c 'echo foo *.nonexistent bar'
61## STDOUT:
62foo *.nonexistent bar
63foo bar
64## END
65## N-I dash/mksh stdout-json: ""
66## N-I dash status: 2
67## N-I mksh status: 1
68
69#### set -o vi/emacs
70set -o vi
71echo $?
72set -o emacs
73echo $?
74## STDOUT:
750
760
77## END
78
79#### vi and emacs are mutually exclusive
80show() {
81 shopt -o -p | egrep 'emacs$|vi$'
82 echo ___
83};
84show
85
86set -o emacs
87show
88
89set -o vi
90show
91
92## STDOUT:
93set +o emacs
94set +o vi
95___
96set -o emacs
97set +o vi
98___
99set +o emacs
100set -o vi
101___
102## END
103## N-I dash/mksh STDOUT:
104___
105___
106___
107## END
108
109#### interactive shell starts with emacs mode on
110case $SH in dash) exit ;; esac
111case $SH in bash|*osh) flag='--rcfile /dev/null' ;; esac
112
113code='test -o emacs; echo $?; test -o vi; echo $?'
114
115echo non-interactive
116$SH $flag -c "$code"
117
118echo interactive
119$SH $flag -i -c "$code"
120
121## STDOUT:
122non-interactive
1231
1241
125interactive
1260
1271
128## END
129## OK mksh STDOUT:
130non-interactive
1310
1321
133interactive
1340
1351
136## END
137## N-I dash stdout-json: ""
138
139#### nounset
140echo "[$unset]"
141set -o nounset
142echo "[$unset]"
143echo end # never reached
144## stdout: []
145## status: 1
146## OK dash status: 2
147
148#### -u is nounset
149echo "[$unset]"
150set -u
151echo "[$unset]"
152echo end # never reached
153## stdout: []
154## status: 1
155## OK dash status: 2
156
157#### -n for no execution (useful with --ast-output)
158# NOTE: set +n doesn't work because nothing is executed!
159echo 1
160set -n
161echo 2
162set +n
163echo 3
164# osh doesn't work because it only checks -n in bin/oil.py?
165## STDOUT:
1661
167## END
168## status: 0
169
170#### pipefail
171# NOTE: the sleeps are because osh can fail non-deterministically because of a
172# bug. Same problem as PIPESTATUS.
173{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
174echo $?
175set -o pipefail
176{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
177echo $?
178## STDOUT:
1790
1802
181## END
182## status: 0
183## N-I dash STDOUT:
1840
185## END
186## N-I dash status: 2
187
188#### shopt -p -o prints 'set' options
189case $SH in dash|mksh) exit ;; esac
190
191shopt -po nounset
192set -o nounset
193shopt -po nounset
194
195echo --
196
197shopt -po | egrep -o 'errexit|noglob|nounset'
198
199## STDOUT:
200set +o nounset
201set -o nounset
202--
203errexit
204noglob
205nounset
206## END
207## N-I dash/mksh STDOUT:
208## END
209
210#### shopt -o prints 'set' options
211case $SH in dash|mksh) exit ;; esac
212
213shopt -o | egrep -o 'errexit|noglob|nounset'
214echo --
215## STDOUT:
216errexit
217noglob
218nounset
219--
220## END
221## N-I dash/mksh STDOUT:
222## END
223
224#### shopt -p prints 'shopt' options
225shopt -p nullglob
226shopt -s nullglob
227shopt -p nullglob
228## STDOUT:
229shopt -u nullglob
230shopt -s nullglob
231## END
232## N-I dash/mksh stdout-json: ""
233## N-I dash/mksh status: 127
234
235#### shopt with no flags prints options
236cd $TMP
237
238# print specific options. OSH does it in a different format.
239shopt nullglob failglob > one.txt
240wc -l one.txt
241grep -o nullglob one.txt
242grep -o failglob one.txt
243
244# print all options
245shopt | grep nullglob | wc -l
246## STDOUT:
2472 one.txt
248nullglob
249failglob
2501
251## END
252## N-I dash/mksh STDOUT:
2530 one.txt
2540
255## END
256
257#### noclobber off
258set -o errexit
259
260echo foo > can-clobber
261echo status=$?
262set +C
263
264echo foo > can-clobber
265echo status=$?
266set +o noclobber
267
268echo foo > can-clobber
269echo status=$?
270cat can-clobber
271
272## STDOUT:
273status=0
274status=0
275status=0
276foo
277## END
278
279#### noclobber on
280
281rm -f no-clobber
282set -C
283
284echo foo > no-clobber
285echo create=$?
286
287echo overwrite > no-clobber
288echo overwrite=$?
289
290echo force >| no-clobber
291echo force=$?
292
293cat no-clobber
294
295## STDOUT:
296create=0
297overwrite=1
298force=0
299force
300## END
301## OK dash STDOUT:
302create=0
303overwrite=2
304force=0
305force
306## END
307
308#### noclobber on <>
309set -C
310echo foo >| $TMP/no-clobber
311exec 3<> $TMP/no-clobber
312read -n 1 <&3
313echo -n . >&3
314exec 3>&-
315cat $TMP/no-clobber
316## STDOUT:
317f.o
318## END
319## N-I dash STDOUT:
320.oo
321## END
322
323#### noclobber on >>
324rm -f $TMP/no-clobber
325
326set -C
327echo foo >> $TMP/no-clobber
328echo status=$?
329
330cat $TMP/no-clobber
331## STDOUT:
332status=0
333foo
334## END
335
336#### noclobber on &> >
337case $SH in dash) exit ;; esac
338
339set -C
340
341rm -f $TMP/no-clobber
342echo foo > $TMP/no-clobber
343echo stdout=$?
344echo bar > $TMP/no-clobber
345echo again=$?
346cat $TMP/no-clobber
347
348rm -f $TMP/no-clobber
349echo baz &> $TMP/no-clobber
350echo both=$?
351echo foo &> $TMP/no-clobber
352echo again=$?
353cat $TMP/no-clobber
354
355## STDOUT:
356stdout=0
357again=1
358foo
359both=0
360again=1
361baz
362## END
363## BUG dash STDOUT:
364## END
365
366#### noclobber on &>> >>
367set -C
368
369rm -f $TMP/no-clobber
370echo foo >> $TMP/no-clobber
371echo stdout=$?
372echo bar >> $TMP/no-clobber
373echo again=$?
374cat $TMP/no-clobber
375
376rm -f $TMP/no-clobber
377echo baz &>> $TMP/no-clobber
378echo both=$?
379echo foo &>> $TMP/no-clobber
380echo again=$?
381cat $TMP/no-clobber
382
383## STDOUT:
384stdout=0
385again=0
386foo
387bar
388both=0
389again=0
390baz
391foo
392## END
393## BUG dash STDOUT:
394stdout=0
395again=0
396foo
397bar
398both=0
399baz
400again=0
401foo
402## END
403
404#### set without args lists variables
405__GLOBAL=g
406f() {
407 local __mylocal=L
408 local __OTHERLOCAL=L
409 __GLOBAL=mutated
410 set | grep '^__'
411}
412g() {
413 local __var_in_parent_scope=D
414 f
415}
416g
417## status: 0
418## STDOUT:
419__GLOBAL=mutated
420__OTHERLOCAL=L
421__mylocal=L
422__var_in_parent_scope=D
423## END
424## OK mksh STDOUT:
425__GLOBAL=mutated
426__var_in_parent_scope=D
427__OTHERLOCAL=L
428__mylocal=L
429## END
430## OK dash STDOUT:
431__GLOBAL='mutated'
432__OTHERLOCAL='L'
433__mylocal='L'
434__var_in_parent_scope='D'
435## END
436
437#### set without args and array variables
438declare -a __array
439__array=(1 2 '3 4')
440set | grep '^__'
441## STDOUT:
442__array=(1 2 '3 4')
443## END
444## OK bash STDOUT:
445__array=([0]="1" [1]="2" [2]="3 4")
446## END
447## OK mksh STDOUT:
448__array[0]=1
449__array[1]=2
450__array[2]='3 4'
451## END
452## OK zsh STDOUT:
453a=( 1 2 3 )
454## END
455## N-I dash stdout-json: ""
456## N-I dash status: 2
457
458#### set without args and assoc array variables (not in OSH)
459typeset -A __assoc
460__assoc['k e y']='v a l'
461__assoc[a]=b
462set | grep '^__'
463## STDOUT:
464__assoc=([a]="b" ["k e y"]="v a l" )
465## END
466## N-I mksh stdout-json: ""
467## N-I mksh status: 1
468## N-I dash stdout-json: ""
469## N-I dash status: 1
470## N-I osh stdout-json: ""
471## N-I osh status: 1
472
473#### shopt -q
474shopt -q nullglob
475echo nullglob=$?
476
477# set it
478shopt -s nullglob
479
480shopt -q nullglob
481echo nullglob=$?
482
483shopt -q nullglob failglob
484echo nullglob,failglob=$?
485
486# set it
487shopt -s failglob
488shopt -q nullglob failglob
489echo nullglob,failglob=$?
490
491## STDOUT:
492nullglob=1
493nullglob=0
494nullglob,failglob=1
495nullglob,failglob=0
496## END
497## N-I dash/mksh STDOUT:
498nullglob=127
499nullglob=127
500nullglob,failglob=127
501nullglob,failglob=127
502## END
503
504#### shopt -q invalid
505shopt -q invalidZZ
506echo invalidZZ=$?
507## STDOUT:
508invalidZZ=2
509## END
510## OK bash STDOUT:
511invalidZZ=1
512## END
513## N-I dash/mksh STDOUT:
514invalidZZ=127
515## END
516
517#### shopt -s strict:all
518n=2
519
520show-strict() {
521 shopt -p | grep 'strict_' | head -n $n
522 echo -
523}
524
525show-strict
526shopt -s strict:all
527show-strict
528shopt -u strict_argv
529show-strict
530## STDOUT:
531shopt -u strict_arg_parse
532shopt -u strict_argv
533-
534shopt -s strict_arg_parse
535shopt -s strict_argv
536-
537shopt -s strict_arg_parse
538shopt -u strict_argv
539-
540## END
541## N-I dash status: 2
542## N-I dash stdout-json: ""
543## N-I bash/mksh STDOUT:
544-
545-
546-
547## END
548
549#### shopt allows for backward compatibility like bash
550
551# doesn't have to be on, but just for testing
552set -o errexit
553
554shopt -p nullglob || true # bash returns 1 here? Like -q.
555
556# This should set nullglob, and return 1, which can be ignored
557shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
558echo status=$?
559
560shopt -p nullglob || true
561
562## STDOUT:
563shopt -u nullglob
564status=0
565shopt -s nullglob
566## END
567## N-I dash/mksh STDOUT:
568status=0
569## END
570## N-I dash/mksh status: 0
571
572#### shopt -p validates option names
573shopt -p nullglob invalid failglob
574echo status=$?
575# same thing as -p, slightly different format in bash
576shopt nullglob invalid failglob > $TMP/out.txt
577status=$?
578sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
579echo status=$status
580## STDOUT:
581status=2
582status=2
583## END
584## OK bash STDOUT:
585shopt -u nullglob
586shopt -u failglob
587status=1
588nullglob off
589failglob off
590status=1
591## END
592## N-I dash/mksh STDOUT:
593status=127
594status=127
595## END
596
597#### shopt -p -o validates option names
598shopt -p -o errexit invalid nounset
599echo status=$?
600## STDOUT:
601set +o errexit
602status=2
603## END
604## OK bash STDOUT:
605set +o errexit
606set +o nounset
607status=1
608## END
609## N-I dash/mksh STDOUT:
610status=127
611## END
612
613#### stubbed out bash options
614shopt -s ignore_shopt_not_impl
615for name in foo autocd cdable_vars checkwinsize; do
616 shopt -s $name
617 echo $?
618done
619## STDOUT:
6202
6210
6220
6230
624## END
625## OK bash STDOUT:
6261
6270
6280
6290
630## END
631## OK dash/mksh STDOUT:
632127
633127
634127
635127
636## END
637
638#### shopt -s nounset works in YSH, not in bash
639case $SH in
640 *dash|*mksh)
641 echo N-I
642 exit
643 ;;
644esac
645shopt -s nounset
646echo status=$?
647
648# get rid of extra space in bash output
649set -o | grep nounset | sed 's/[ \t]\+/ /g'
650
651## STDOUT:
652status=0
653set -o nounset
654## END
655## OK bash STDOUT:
656status=1
657nounset off
658# END
659## N-I dash/mksh STDOUT:
660N-I
661## END
662
663#### Unimplemented options - print, query, set, unset
664case $SH in dash|mksh) exit ;; esac
665
666opt_name=xpg_echo
667
668shopt -p xpg_echo
669shopt -q xpg_echo; echo q=$?
670
671shopt -s xpg_echo
672shopt -p xpg_echo
673
674shopt -u xpg_echo
675shopt -p xpg_echo
676echo p=$? # weird, bash also returns a status
677
678shopt xpg_echo >/dev/null
679echo noflag=$?
680
681shopt -o errexit >/dev/null
682echo set=$?
683
684## STDOUT:
685q=2
686p=2
687noflag=2
688set=1
689## END
690
691## OK bash STDOUT:
692shopt -u xpg_echo
693q=1
694shopt -s xpg_echo
695shopt -u xpg_echo
696p=1
697noflag=1
698set=1
699## END
700
701## N-I dash/mksh STDOUT:
702## END
703
704#### Unimplemented options - OSH shopt -s ignore_shopt_not_impl
705case $SH in dash|mksh) exit ;; esac
706
707shopt -s ignore_shopt_not_impl
708
709opt_name=xpg_echo
710
711shopt -p xpg_echo
712shopt -q xpg_echo; echo q=$?
713
714shopt -s xpg_echo
715shopt -p xpg_echo
716
717shopt -u xpg_echo
718shopt -p xpg_echo
719echo p=$? # weird, bash also returns a status
720
721shopt xpg_echo >/dev/null
722echo noflag=$?
723
724shopt -o errexit >/dev/null
725echo set=$?
726
727## STDOUT:
728shopt -u xpg_echo
729q=1
730shopt -s xpg_echo
731shopt -u xpg_echo
732p=1
733noflag=1
734set=1
735## END
736
737## N-I dash/mksh STDOUT:
738## END
739
740#### shopt -p exit code (regression)
741case $SH in dash|mksh) exit ;; esac
742
743shopt -p > /dev/null
744echo status=$?
745
746## STDOUT:
747status=0
748## END
749
750## N-I dash/mksh STDOUT:
751## END
752
753#### no-ops not shown by shopt -p
754
755shopt -p | grep xpg
756echo --
757## STDOUT:
758--
759## END
760## OK bash STDOUT:
761shopt -u xpg_echo
762--
763## END
764
765