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

760 lines, 418 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 &>> >>
367case $SH in dash) echo 'flaky'; exit ;; esac
368
369set -C
370
371rm -f $TMP/no-clobber
372echo foo >> $TMP/no-clobber
373echo stdout=$?
374echo bar >> $TMP/no-clobber
375echo again=$?
376cat $TMP/no-clobber
377
378rm -f $TMP/no-clobber
379echo baz &>> $TMP/no-clobber
380echo both=$?
381echo foo &>> $TMP/no-clobber
382echo again=$?
383cat $TMP/no-clobber
384
385## STDOUT:
386stdout=0
387again=0
388foo
389bar
390both=0
391again=0
392baz
393foo
394## END
395## BUG dash STDOUT:
396flaky
397## END
398
399#### set without args lists variables
400__GLOBAL=g
401f() {
402 local __mylocal=L
403 local __OTHERLOCAL=L
404 __GLOBAL=mutated
405 set | grep '^__'
406}
407g() {
408 local __var_in_parent_scope=D
409 f
410}
411g
412## status: 0
413## STDOUT:
414__GLOBAL=mutated
415__OTHERLOCAL=L
416__mylocal=L
417__var_in_parent_scope=D
418## END
419## OK mksh STDOUT:
420__GLOBAL=mutated
421__var_in_parent_scope=D
422__OTHERLOCAL=L
423__mylocal=L
424## END
425## OK dash STDOUT:
426__GLOBAL='mutated'
427__OTHERLOCAL='L'
428__mylocal='L'
429__var_in_parent_scope='D'
430## END
431
432#### set without args and array variables
433declare -a __array
434__array=(1 2 '3 4')
435set | grep '^__'
436## STDOUT:
437__array=(1 2 '3 4')
438## END
439## OK bash STDOUT:
440__array=([0]="1" [1]="2" [2]="3 4")
441## END
442## OK mksh STDOUT:
443__array[0]=1
444__array[1]=2
445__array[2]='3 4'
446## END
447## OK zsh STDOUT:
448a=( 1 2 3 )
449## END
450## N-I dash stdout-json: ""
451## N-I dash status: 2
452
453#### set without args and assoc array variables (not in OSH)
454typeset -A __assoc
455__assoc['k e y']='v a l'
456__assoc[a]=b
457set | grep '^__'
458## STDOUT:
459__assoc=([a]="b" ["k e y"]="v a l" )
460## END
461## N-I mksh stdout-json: ""
462## N-I mksh status: 1
463## N-I dash stdout-json: ""
464## N-I dash status: 1
465## N-I osh stdout-json: ""
466## N-I osh status: 1
467
468#### shopt -q
469shopt -q nullglob
470echo nullglob=$?
471
472# set it
473shopt -s nullglob
474
475shopt -q nullglob
476echo nullglob=$?
477
478shopt -q nullglob failglob
479echo nullglob,failglob=$?
480
481# set it
482shopt -s failglob
483shopt -q nullglob failglob
484echo nullglob,failglob=$?
485
486## STDOUT:
487nullglob=1
488nullglob=0
489nullglob,failglob=1
490nullglob,failglob=0
491## END
492## N-I dash/mksh STDOUT:
493nullglob=127
494nullglob=127
495nullglob,failglob=127
496nullglob,failglob=127
497## END
498
499#### shopt -q invalid
500shopt -q invalidZZ
501echo invalidZZ=$?
502## STDOUT:
503invalidZZ=2
504## END
505## OK bash STDOUT:
506invalidZZ=1
507## END
508## N-I dash/mksh STDOUT:
509invalidZZ=127
510## END
511
512#### shopt -s strict:all
513n=2
514
515show-strict() {
516 shopt -p | grep 'strict_' | head -n $n
517 echo -
518}
519
520show-strict
521shopt -s strict:all
522show-strict
523shopt -u strict_argv
524show-strict
525## STDOUT:
526shopt -u strict_arg_parse
527shopt -u strict_argv
528-
529shopt -s strict_arg_parse
530shopt -s strict_argv
531-
532shopt -s strict_arg_parse
533shopt -u strict_argv
534-
535## END
536## N-I dash status: 2
537## N-I dash stdout-json: ""
538## N-I bash/mksh STDOUT:
539-
540-
541-
542## END
543
544#### shopt allows for backward compatibility like bash
545
546# doesn't have to be on, but just for testing
547set -o errexit
548
549shopt -p nullglob || true # bash returns 1 here? Like -q.
550
551# This should set nullglob, and return 1, which can be ignored
552shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
553echo status=$?
554
555shopt -p nullglob || true
556
557## STDOUT:
558shopt -u nullglob
559status=0
560shopt -s nullglob
561## END
562## N-I dash/mksh STDOUT:
563status=0
564## END
565## N-I dash/mksh status: 0
566
567#### shopt -p validates option names
568shopt -p nullglob invalid failglob
569echo status=$?
570# same thing as -p, slightly different format in bash
571shopt nullglob invalid failglob > $TMP/out.txt
572status=$?
573sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
574echo status=$status
575## STDOUT:
576status=2
577status=2
578## END
579## OK bash STDOUT:
580shopt -u nullglob
581shopt -u failglob
582status=1
583nullglob off
584failglob off
585status=1
586## END
587## N-I dash/mksh STDOUT:
588status=127
589status=127
590## END
591
592#### shopt -p -o validates option names
593shopt -p -o errexit invalid nounset
594echo status=$?
595## STDOUT:
596set +o errexit
597status=2
598## END
599## OK bash STDOUT:
600set +o errexit
601set +o nounset
602status=1
603## END
604## N-I dash/mksh STDOUT:
605status=127
606## END
607
608#### stubbed out bash options
609shopt -s ignore_shopt_not_impl
610for name in foo autocd cdable_vars checkwinsize; do
611 shopt -s $name
612 echo $?
613done
614## STDOUT:
6152
6160
6170
6180
619## END
620## OK bash STDOUT:
6211
6220
6230
6240
625## END
626## OK dash/mksh STDOUT:
627127
628127
629127
630127
631## END
632
633#### shopt -s nounset works in YSH, not in bash
634case $SH in
635 *dash|*mksh)
636 echo N-I
637 exit
638 ;;
639esac
640shopt -s nounset
641echo status=$?
642
643# get rid of extra space in bash output
644set -o | grep nounset | sed 's/[ \t]\+/ /g'
645
646## STDOUT:
647status=0
648set -o nounset
649## END
650## OK bash STDOUT:
651status=1
652nounset off
653# END
654## N-I dash/mksh STDOUT:
655N-I
656## END
657
658#### Unimplemented options - print, query, set, unset
659case $SH in dash|mksh) exit ;; esac
660
661opt_name=xpg_echo
662
663shopt -p xpg_echo
664shopt -q xpg_echo; echo q=$?
665
666shopt -s xpg_echo
667shopt -p xpg_echo
668
669shopt -u xpg_echo
670shopt -p xpg_echo
671echo p=$? # weird, bash also returns a status
672
673shopt xpg_echo >/dev/null
674echo noflag=$?
675
676shopt -o errexit >/dev/null
677echo set=$?
678
679## STDOUT:
680q=2
681p=2
682noflag=2
683set=1
684## END
685
686## OK bash STDOUT:
687shopt -u xpg_echo
688q=1
689shopt -s xpg_echo
690shopt -u xpg_echo
691p=1
692noflag=1
693set=1
694## END
695
696## N-I dash/mksh STDOUT:
697## END
698
699#### Unimplemented options - OSH shopt -s ignore_shopt_not_impl
700case $SH in dash|mksh) exit ;; esac
701
702shopt -s ignore_shopt_not_impl
703
704opt_name=xpg_echo
705
706shopt -p xpg_echo
707shopt -q xpg_echo; echo q=$?
708
709shopt -s xpg_echo
710shopt -p xpg_echo
711
712shopt -u xpg_echo
713shopt -p xpg_echo
714echo p=$? # weird, bash also returns a status
715
716shopt xpg_echo >/dev/null
717echo noflag=$?
718
719shopt -o errexit >/dev/null
720echo set=$?
721
722## STDOUT:
723shopt -u xpg_echo
724q=1
725shopt -s xpg_echo
726shopt -u xpg_echo
727p=1
728noflag=1
729set=1
730## END
731
732## N-I dash/mksh STDOUT:
733## END
734
735#### shopt -p exit code (regression)
736case $SH in dash|mksh) exit ;; esac
737
738shopt -p > /dev/null
739echo status=$?
740
741## STDOUT:
742status=0
743## END
744
745## N-I dash/mksh STDOUT:
746## END
747
748#### no-ops not shown by shopt -p
749
750shopt -p | grep xpg
751echo --
752## STDOUT:
753--
754## END
755## OK bash STDOUT:
756shopt -u xpg_echo
757--
758## END
759
760