OILS / spec / sh-options.test.sh View on Github | oilshell.org

697 lines, 374 significant
1# Test set flags, sh flags.
2
3## compare_shells: bash dash mksh
4## oils_failures_allowed: 2
5## tags: interactive
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#### can continue after unknown option
70# dash and mksh make this a fatal error no matter what.
71set -o errexit
72set -o STRICT || true # unknown option
73echo hello
74## stdout: hello
75## status: 0
76## BUG dash/mksh stdout-json: ""
77## BUG dash status: 2
78## BUG mksh status: 1
79
80#### set with both options and argv
81set -o errexit a b c
82echo "$@"
83false
84echo done
85## stdout: a b c
86## status: 1
87
88#### set -o vi/emacs
89set -o vi
90echo $?
91set -o emacs
92echo $?
93## STDOUT:
940
950
96## END
97
98#### vi and emacs are mutually exclusive
99show() {
100 shopt -o -p | egrep 'emacs$|vi$'
101 echo ___
102};
103show
104
105set -o emacs
106show
107
108set -o vi
109show
110
111## STDOUT:
112set +o emacs
113set +o vi
114___
115set -o emacs
116set +o vi
117___
118set +o emacs
119set -o vi
120___
121## END
122## N-I dash/mksh STDOUT:
123___
124___
125___
126## END
127
128#### interactive shell starts with emacs mode on
129case $SH in (dash) exit ;; esac
130case $SH in (bash|*osh) flag='--rcfile /dev/null' ;; esac
131
132code='test -o emacs; echo $?; test -o vi; echo $?'
133
134echo non-interactive
135$SH $flag -c "$code"
136
137echo interactive
138$SH $flag -i -c "$code"
139
140## STDOUT:
141non-interactive
1421
1431
144interactive
1450
1461
147## END
148## OK mksh STDOUT:
149non-interactive
1500
1511
152interactive
1530
1541
155## END
156## N-I dash stdout-json: ""
157
158#### nounset
159echo "[$unset]"
160set -o nounset
161echo "[$unset]"
162echo end # never reached
163## stdout: []
164## status: 1
165## OK dash status: 2
166
167#### -u is nounset
168echo "[$unset]"
169set -u
170echo "[$unset]"
171echo end # never reached
172## stdout: []
173## status: 1
174## OK dash status: 2
175
176#### nounset with "$@"
177set a b c
178set -u # shouldn't touch argv
179echo "$@"
180## stdout: a b c
181
182#### set -u -- clears argv
183set a b c
184set -u -- # shouldn't touch argv
185echo "$@"
186## stdout:
187
188#### set -u -- x y z
189set a b c
190set -u -- x y z
191echo "$@"
192## stdout: x y z
193
194#### reset option with long flag
195set -o errexit
196set +o errexit
197echo "[$unset]"
198## stdout: []
199## status: 0
200
201#### reset option with short flag
202set -u
203set +u
204echo "[$unset]"
205## stdout: []
206## status: 0
207
208#### set -eu (flag parsing)
209set -eu
210echo "[$unset]"
211echo status=$?
212## stdout-json: ""
213## status: 1
214## OK dash status: 2
215
216#### -n for no execution (useful with --ast-output)
217# NOTE: set +n doesn't work because nothing is executed!
218echo 1
219set -n
220echo 2
221set +n
222echo 3
223# osh doesn't work because it only checks -n in bin/oil.py?
224## STDOUT:
2251
226## END
227## status: 0
228
229#### pipefail
230# NOTE: the sleeps are because osh can fail non-deterministically because of a
231# bug. Same problem as PIPESTATUS.
232{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
233echo $?
234set -o pipefail
235{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
236echo $?
237## STDOUT:
2380
2392
240## END
241## status: 0
242## N-I dash STDOUT:
2430
244## END
245## N-I dash status: 2
246
247#### shopt -p -o prints 'set' options
248shopt -po nounset
249set -o nounset
250shopt -po nounset
251## STDOUT:
252set +o nounset
253set -o nounset
254## END
255## N-I dash/mksh stdout-json: ""
256## N-I dash/mksh status: 127
257
258#### shopt -p prints 'shopt' options
259shopt -p nullglob
260shopt -s nullglob
261shopt -p nullglob
262## STDOUT:
263shopt -u nullglob
264shopt -s nullglob
265## END
266## N-I dash/mksh stdout-json: ""
267## N-I dash/mksh status: 127
268
269#### shopt with no flags prints options
270cd $TMP
271
272# print specific options. OSH does it in a different format.
273shopt nullglob failglob > one.txt
274wc -l one.txt
275grep -o nullglob one.txt
276grep -o failglob one.txt
277
278# print all options
279shopt | grep nullglob | wc -l
280## STDOUT:
2812 one.txt
282nullglob
283failglob
2841
285## END
286## N-I dash/mksh STDOUT:
2870 one.txt
2880
289## END
290
291#### noclobber off
292set -o errexit
293echo foo > $TMP/can-clobber
294set +C
295echo foo > $TMP/can-clobber
296set +o noclobber
297echo foo > $TMP/can-clobber
298cat $TMP/can-clobber
299## stdout: foo
300
301#### noclobber on
302# Not implemented yet.
303rm $TMP/no-clobber
304set -C
305echo foo > $TMP/no-clobber
306echo $?
307echo foo > $TMP/no-clobber
308echo $?
309## stdout-json: "0\n1\n"
310## OK dash stdout-json: "0\n2\n"
311
312#### SHELLOPTS is updated when options are changed
313echo $SHELLOPTS | grep -q xtrace
314echo $?
315set -x
316echo $SHELLOPTS | grep -q xtrace
317echo $?
318set +x
319echo $SHELLOPTS | grep -q xtrace
320echo $?
321## stdout-json: "1\n0\n1\n"
322## N-I dash/mksh stdout-json: "1\n1\n1\n"
323
324#### SHELLOPTS is readonly
325SHELLOPTS=x
326echo status=$?
327## stdout: status=1
328## N-I dash/mksh stdout: status=0
329
330# Setting a readonly variable in osh is a hard failure.
331## OK osh status: 1
332## OK osh stdout-json: ""
333
334#### set - -
335set a b
336echo "$@"
337set - a b
338echo "$@"
339set -- a b
340echo "$@"
341set - -
342echo "$@"
343set - +
344echo "$@"
345set + -
346echo "$@"
347set -- --
348echo "$@"
349
350# note: zsh is different, and yash is totally different
351## STDOUT:
352a b
353a b
354a b
355-
356+
357+
358--
359## END
360## OK osh/yash STDOUT:
361a b
362- a b
363a b
364- -
365- +
366+ -
367--
368## END
369## BUG mksh STDOUT:
370a b
371a b
372a b
373-
374+
375-
376--
377## END
378## BUG zsh STDOUT:
379a b
380a b
381a b
382
383+
384
385--
386## END
387
388#### set -o lists options
389# NOTE: osh doesn't use the same format yet.
390set -o | grep -o noexec
391## STDOUT:
392noexec
393## END
394
395#### set without args lists variables
396__GLOBAL=g
397f() {
398 local __mylocal=L
399 local __OTHERLOCAL=L
400 __GLOBAL=mutated
401 set | grep '^__'
402}
403g() {
404 local __var_in_parent_scope=D
405 f
406}
407g
408## status: 0
409## STDOUT:
410__GLOBAL=mutated
411__OTHERLOCAL=L
412__mylocal=L
413__var_in_parent_scope=D
414## END
415## OK mksh STDOUT:
416__GLOBAL=mutated
417__var_in_parent_scope=D
418__OTHERLOCAL=L
419__mylocal=L
420## END
421## OK dash STDOUT:
422__GLOBAL='mutated'
423__OTHERLOCAL='L'
424__mylocal='L'
425__var_in_parent_scope='D'
426## END
427
428#### 'set' and 'eval' round trip
429
430# NOTE: not testing arrays and associative arrays!
431_space='[ ]'
432_whitespace=$'[\t\r\n]'
433_sq="'single quotes'"
434_backslash_dq="\\ \""
435_unicode=$'[\u03bc]'
436
437# Save the variables
438varfile=$TMP/vars-$(basename $SH).txt
439
440set | grep '^_' > "$varfile"
441
442# Unset variables
443unset _space _whitespace _sq _backslash_dq _unicode
444echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ]
445
446# Restore them
447
448. $varfile
449echo "Code saved to $varfile" 1>&2 # for debugging
450
451test "$_space" = '[ ]' && echo OK
452test "$_whitespace" = $'[\t\r\n]' && echo OK
453test "$_sq" = "'single quotes'" && echo OK
454test "$_backslash_dq" = "\\ \"" && echo OK
455test "$_unicode" = $'[\u03bc]' && echo OK
456
457## STDOUT:
458[ ]
459OK
460OK
461OK
462OK
463OK
464## END
465
466#### set without args and array variables (not in OSH)
467declare -a __array
468__array=(1 2 '3 4')
469set | grep '^__'
470## STDOUT:
471__array=([0]="1" [1]="2" [2]="3 4")
472## END
473## OK mksh STDOUT:
474__array[0]=1
475__array[1]=2
476__array[2]='3 4'
477## N-I dash stdout-json: ""
478## N-I dash status: 2
479## N-I osh stdout-json: ""
480## N-I osh status: 1
481
482#### set without args and assoc array variables (not in OSH)
483typeset -A __assoc
484__assoc['k e y']='v a l'
485__assoc[a]=b
486set | grep '^__'
487## STDOUT:
488__assoc=([a]="b" ["k e y"]="v a l" )
489## END
490## N-I mksh stdout-json: ""
491## N-I mksh status: 1
492## N-I dash stdout-json: ""
493## N-I dash status: 1
494## N-I osh stdout-json: ""
495## N-I osh status: 1
496
497#### shopt -q
498shopt -q nullglob
499echo nullglob=$?
500
501# set it
502shopt -s nullglob
503
504shopt -q nullglob
505echo nullglob=$?
506
507shopt -q nullglob failglob
508echo nullglob,failglob=$?
509
510# set it
511shopt -s failglob
512shopt -q nullglob failglob
513echo nullglob,failglob=$?
514
515## STDOUT:
516nullglob=1
517nullglob=0
518nullglob,failglob=1
519nullglob,failglob=0
520## END
521## N-I dash/mksh STDOUT:
522nullglob=127
523nullglob=127
524nullglob,failglob=127
525nullglob,failglob=127
526## END
527
528#### shopt -q invalid
529shopt -q invalidZZ
530echo invalidZZ=$?
531## STDOUT:
532invalidZZ=2
533## END
534## OK bash STDOUT:
535invalidZZ=1
536## END
537## N-I dash/mksh STDOUT:
538invalidZZ=127
539## END
540
541#### shopt -s strict:all
542n=2
543
544show-strict() {
545 shopt -p | grep 'strict_' | head -n $n
546 echo -
547}
548
549show-strict
550shopt -s strict:all
551show-strict
552shopt -u strict_arith
553show-strict
554## STDOUT:
555shopt -u strict_argv
556shopt -u strict_arith
557-
558shopt -s strict_argv
559shopt -s strict_arith
560-
561shopt -s strict_argv
562shopt -u strict_arith
563-
564## END
565## N-I dash status: 2
566## N-I dash stdout-json: ""
567## N-I bash/mksh STDOUT:
568-
569-
570-
571## END
572
573#### shopt allows for backward compatibility like bash
574
575# doesn't have to be on, but just for testing
576set -o errexit
577
578shopt -p nullglob || true # bash returns 1 here? Like -q.
579
580# This should set nullglob, and return 1, which can be ignored
581shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
582echo status=$?
583
584shopt -p nullglob || true
585
586## STDOUT:
587shopt -u nullglob
588status=0
589shopt -s nullglob
590## END
591## N-I dash/mksh STDOUT:
592status=0
593## END
594## N-I dash/mksh status: 0
595
596#### shopt -p validates option names
597shopt -p nullglob invalid failglob
598echo status=$?
599# same thing as -p, slightly different format in bash
600shopt nullglob invalid failglob > $TMP/out.txt
601status=$?
602sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
603echo status=$status
604## STDOUT:
605status=2
606status=2
607## END
608## OK bash STDOUT:
609shopt -u nullglob
610shopt -u failglob
611status=1
612nullglob off
613failglob off
614status=1
615## END
616## N-I dash/mksh STDOUT:
617status=127
618status=127
619## END
620
621#### shopt -p -o validates option names
622shopt -p -o errexit invalid nounset
623echo status=$?
624## STDOUT:
625set +o errexit
626status=2
627## END
628## OK bash STDOUT:
629set +o errexit
630set +o nounset
631status=1
632## END
633## N-I dash/mksh STDOUT:
634status=127
635## END
636
637#### stubbed out bash options
638for name in foo autocd cdable_vars checkwinsize; do
639 shopt -s $name
640 echo $?
641done
642## STDOUT:
6432
6440
6450
6460
647## END
648## OK bash STDOUT:
6491
6500
6510
6520
653## END
654## OK dash/mksh STDOUT:
655127
656127
657127
658127
659## END
660
661#### shopt -s nounset works in Oil, not in bash
662case $SH in
663 *dash|*mksh)
664 echo N-I
665 exit
666 ;;
667esac
668shopt -s nounset
669echo status=$?
670
671# get rid of extra space in bash output
672set -o | grep nounset | sed 's/[ \t]\+/ /g'
673
674## STDOUT:
675status=0
676set -o nounset
677## END
678## OK bash STDOUT:
679status=1
680nounset off
681# END
682## N-I dash/mksh STDOUT:
683N-I
684## END
685
686#### no-ops not in shopt -p output
687shopt -p | grep xpg
688echo --
689## STDOUT:
690--
691## END
692## OK bash STDOUT:
693shopt -u xpg_echo
694--
695## END
696
697