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

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