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
17 set -o pipefail -o nounset
18 echo $-
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
27 set -efuC
28 o=$-
29 [[ $o == *e* ]]; echo yes
30 [[ $o == *f* ]]; echo yes
31 [[ $o == *u* ]]; echo yes
32 [[ $o == *C* ]]; echo yes
33 ## STDOUT:
34 yes
35 yes
36 yes
37 yes
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:
46 FALSE
47 TRUE
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:
63 foo *.nonexistent bar
64 foo 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
71 set -o vi
72 echo $?
73 set -o emacs
74 echo $?
75 ## STDOUT:
76 0
77 0
78 ## END
79
80 #### vi and emacs are mutually exclusive
81 show() {
82 shopt -o -p | egrep 'emacs$|vi$'
83 echo ___
84 };
85 show
86
87 set -o emacs
88 show
89
90 set -o vi
91 show
92
93 ## STDOUT:
94 set +o emacs
95 set +o vi
96 ___
97 set -o emacs
98 set +o vi
99 ___
100 set +o emacs
101 set -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
111 case $SH in dash) exit ;; esac
112 case $SH in bash|*osh) flag='--rcfile /dev/null' ;; esac
113
114 code='test -o emacs; echo $?; test -o vi; echo $?'
115
116 echo non-interactive
117 $SH $flag -c "$code"
118
119 echo interactive
120 $SH $flag -i -c "$code"
121
122 ## STDOUT:
123 non-interactive
124 1
125 1
126 interactive
127 0
128 1
129 ## END
130 ## OK mksh STDOUT:
131 non-interactive
132 0
133 1
134 interactive
135 0
136 1
137 ## END
138 ## N-I dash stdout-json: ""
139
140 #### nounset
141 echo "[$unset]"
142 set -o nounset
143 echo "[$unset]"
144 echo end # never reached
145 ## stdout: []
146 ## status: 1
147 ## OK dash status: 2
148
149 #### -u is nounset
150 echo "[$unset]"
151 set -u
152 echo "[$unset]"
153 echo 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!
160 echo 1
161 set -n
162 echo 2
163 set +n
164 echo 3
165 # osh doesn't work because it only checks -n in bin/oil.py?
166 ## STDOUT:
167 1
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; }
175 echo $?
176 set -o pipefail
177 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
178 echo $?
179 ## STDOUT:
180 0
181 2
182 ## END
183 ## status: 0
184 ## N-I dash STDOUT:
185 0
186 ## END
187 ## N-I dash status: 2
188
189 #### shopt -p -o prints 'set' options
190 case $SH in dash|mksh) exit ;; esac
191
192 shopt -po nounset
193 set -o nounset
194 shopt -po nounset
195
196 echo --
197
198 shopt -po | egrep -o 'errexit|noglob|nounset'
199
200 ## STDOUT:
201 set +o nounset
202 set -o nounset
203 --
204 errexit
205 noglob
206 nounset
207 ## END
208 ## N-I dash/mksh STDOUT:
209 ## END
210
211 #### shopt -o prints 'set' options
212 case $SH in dash|mksh) exit ;; esac
213
214 shopt -o | egrep -o 'errexit|noglob|nounset'
215 echo --
216 ## STDOUT:
217 errexit
218 noglob
219 nounset
220 --
221 ## END
222 ## N-I dash/mksh STDOUT:
223 ## END
224
225 #### shopt -p prints 'shopt' options
226 shopt -p nullglob
227 shopt -s nullglob
228 shopt -p nullglob
229 ## STDOUT:
230 shopt -u nullglob
231 shopt -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
237 cd $TMP
238
239 # print specific options. OSH does it in a different format.
240 shopt nullglob failglob > one.txt
241 wc -l one.txt
242 grep -o nullglob one.txt
243 grep -o failglob one.txt
244
245 # print all options
246 shopt | grep nullglob | wc -l
247 ## STDOUT:
248 2 one.txt
249 nullglob
250 failglob
251 1
252 ## END
253 ## N-I dash/mksh STDOUT:
254 0 one.txt
255 0
256 ## END
257
258 #### noclobber off
259 set -o errexit
260
261 echo foo > can-clobber
262 echo status=$?
263 set +C
264
265 echo foo > can-clobber
266 echo status=$?
267 set +o noclobber
268
269 echo foo > can-clobber
270 echo status=$?
271 cat can-clobber
272
273 ## STDOUT:
274 status=0
275 status=0
276 status=0
277 foo
278 ## END
279
280 #### noclobber on
281
282 rm -f no-clobber
283 set -C
284
285 echo foo > no-clobber
286 echo create=$?
287
288 echo overwrite > no-clobber
289 echo overwrite=$?
290
291 echo force >| no-clobber
292 echo force=$?
293
294 cat no-clobber
295
296 ## STDOUT:
297 create=0
298 overwrite=1
299 force=0
300 force
301 ## END
302 ## OK dash STDOUT:
303 create=0
304 overwrite=2
305 force=0
306 force
307 ## END
308
309 #### noclobber on <>
310 set -C
311 echo foo >| $TMP/no-clobber
312 exec 3<> $TMP/no-clobber
313 read -n 1 <&3
314 echo -n . >&3
315 exec 3>&-
316 cat $TMP/no-clobber
317 ## STDOUT:
318 f.o
319 ## END
320 ## N-I dash STDOUT:
321 .oo
322 ## END
323
324 #### noclobber on >>
325 rm -f $TMP/no-clobber
326
327 set -C
328 echo foo >> $TMP/no-clobber
329 echo status=$?
330
331 cat $TMP/no-clobber
332 ## STDOUT:
333 status=0
334 foo
335 ## END
336
337 #### noclobber on &> >
338 case $SH in dash) exit ;; esac
339
340 set -C
341
342 rm -f $TMP/no-clobber
343 echo foo > $TMP/no-clobber
344 echo stdout=$?
345 echo bar > $TMP/no-clobber
346 echo again=$?
347 cat $TMP/no-clobber
348
349 rm -f $TMP/no-clobber
350 echo baz &> $TMP/no-clobber
351 echo both=$?
352 echo foo &> $TMP/no-clobber
353 echo again=$?
354 cat $TMP/no-clobber
355
356 ## STDOUT:
357 stdout=0
358 again=1
359 foo
360 both=0
361 again=1
362 baz
363 ## END
364 ## BUG dash STDOUT:
365 ## END
366
367 #### noclobber on &>> >>
368 case $SH in dash) echo 'flaky'; exit ;; esac
369
370 set -C
371
372 rm -f $TMP/no-clobber
373 echo foo >> $TMP/no-clobber
374 echo stdout=$?
375 echo bar >> $TMP/no-clobber
376 echo again=$?
377 cat $TMP/no-clobber
378
379 rm -f $TMP/no-clobber
380 echo baz &>> $TMP/no-clobber
381 echo both=$?
382 echo foo &>> $TMP/no-clobber
383 echo again=$?
384 cat $TMP/no-clobber
385
386 ## STDOUT:
387 stdout=0
388 again=0
389 foo
390 bar
391 both=0
392 again=0
393 baz
394 foo
395 ## END
396 ## BUG dash STDOUT:
397 flaky
398 ## END
399
400 #### set without args lists variables
401 __GLOBAL=g
402 f() {
403 local __mylocal=L
404 local __OTHERLOCAL=L
405 __GLOBAL=mutated
406 set | grep '^__'
407 }
408 g() {
409 local __var_in_parent_scope=D
410 f
411 }
412 g
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
434 declare -a __array
435 __array=(1 2 '3 4')
436 set | 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:
449 a=( 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)
455 typeset -A __assoc
456 __assoc['k e y']='v a l'
457 __assoc[a]=b
458 set | 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
470 shopt -q nullglob
471 echo nullglob=$?
472
473 # set it
474 shopt -s nullglob
475
476 shopt -q nullglob
477 echo nullglob=$?
478
479 shopt -q nullglob failglob
480 echo nullglob,failglob=$?
481
482 # set it
483 shopt -s failglob
484 shopt -q nullglob failglob
485 echo nullglob,failglob=$?
486
487 ## STDOUT:
488 nullglob=1
489 nullglob=0
490 nullglob,failglob=1
491 nullglob,failglob=0
492 ## END
493 ## N-I dash/mksh STDOUT:
494 nullglob=127
495 nullglob=127
496 nullglob,failglob=127
497 nullglob,failglob=127
498 ## END
499
500 #### shopt -q invalid
501 shopt -q invalidZZ
502 echo invalidZZ=$?
503 ## STDOUT:
504 invalidZZ=2
505 ## END
506 ## OK bash STDOUT:
507 invalidZZ=1
508 ## END
509 ## N-I dash/mksh STDOUT:
510 invalidZZ=127
511 ## END
512
513 #### shopt -s strict:all
514 n=2
515
516 show-strict() {
517 shopt -p | grep 'strict_' | head -n $n
518 echo -
519 }
520
521 show-strict
522 shopt -s strict:all
523 show-strict
524 shopt -u strict_argv
525 show-strict
526 ## STDOUT:
527 shopt -u strict_arg_parse
528 shopt -u strict_argv
529 -
530 shopt -s strict_arg_parse
531 shopt -s strict_argv
532 -
533 shopt -s strict_arg_parse
534 shopt -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
548 set -o errexit
549
550 shopt -p nullglob || true # bash returns 1 here? Like -q.
551
552 # This should set nullglob, and return 1, which can be ignored
553 shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
554 echo status=$?
555
556 shopt -p nullglob || true
557
558 ## STDOUT:
559 shopt -u nullglob
560 status=0
561 shopt -s nullglob
562 ## END
563 ## N-I dash/mksh STDOUT:
564 status=0
565 ## END
566 ## N-I dash/mksh status: 0
567
568 #### shopt -p validates option names
569 shopt -p nullglob invalid failglob
570 echo status=$?
571 # same thing as -p, slightly different format in bash
572 shopt nullglob invalid failglob > $TMP/out.txt
573 status=$?
574 sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
575 echo status=$status
576 ## STDOUT:
577 status=2
578 status=2
579 ## END
580 ## OK bash STDOUT:
581 shopt -u nullglob
582 shopt -u failglob
583 status=1
584 nullglob off
585 failglob off
586 status=1
587 ## END
588 ## N-I dash/mksh STDOUT:
589 status=127
590 status=127
591 ## END
592
593 #### shopt -p -o validates option names
594 shopt -p -o errexit invalid nounset
595 echo status=$?
596 ## STDOUT:
597 set +o errexit
598 status=2
599 ## END
600 ## OK bash STDOUT:
601 set +o errexit
602 set +o nounset
603 status=1
604 ## END
605 ## N-I dash/mksh STDOUT:
606 status=127
607 ## END
608
609 #### stubbed out bash options
610 shopt -s ignore_shopt_not_impl
611 for name in foo autocd cdable_vars checkwinsize; do
612 shopt -s $name
613 echo $?
614 done
615 ## STDOUT:
616 2
617 0
618 0
619 0
620 ## END
621 ## OK bash STDOUT:
622 1
623 0
624 0
625 0
626 ## END
627 ## OK dash/mksh STDOUT:
628 127
629 127
630 127
631 127
632 ## END
633
634 #### shopt -s nounset works in YSH, not in bash
635 case $SH in
636 *dash|*mksh)
637 echo N-I
638 exit
639 ;;
640 esac
641 shopt -s nounset
642 echo status=$?
643
644 # get rid of extra space in bash output
645 set -o | grep nounset | sed 's/[ \t]\+/ /g'
646
647 ## STDOUT:
648 status=0
649 set -o nounset
650 ## END
651 ## OK bash STDOUT:
652 status=1
653 nounset off
654 # END
655 ## N-I dash/mksh STDOUT:
656 N-I
657 ## END
658
659 #### Unimplemented options - print, query, set, unset
660 case $SH in dash|mksh) exit ;; esac
661
662 opt_name=xpg_echo
663
664 shopt -p xpg_echo
665 shopt -q xpg_echo; echo q=$?
666
667 shopt -s xpg_echo
668 shopt -p xpg_echo
669
670 shopt -u xpg_echo
671 shopt -p xpg_echo
672 echo p=$? # weird, bash also returns a status
673
674 shopt xpg_echo >/dev/null
675 echo noflag=$?
676
677 shopt -o errexit >/dev/null
678 echo set=$?
679
680 ## STDOUT:
681 q=2
682 p=2
683 noflag=2
684 set=1
685 ## END
686
687 ## OK bash STDOUT:
688 shopt -u xpg_echo
689 q=1
690 shopt -s xpg_echo
691 shopt -u xpg_echo
692 p=1
693 noflag=1
694 set=1
695 ## END
696
697 ## N-I dash/mksh STDOUT:
698 ## END
699
700 #### Unimplemented options - OSH shopt -s ignore_shopt_not_impl
701 case $SH in dash|mksh) exit ;; esac
702
703 shopt -s ignore_shopt_not_impl
704
705 opt_name=xpg_echo
706
707 shopt -p xpg_echo
708 shopt -q xpg_echo; echo q=$?
709
710 shopt -s xpg_echo
711 shopt -p xpg_echo
712
713 shopt -u xpg_echo
714 shopt -p xpg_echo
715 echo p=$? # weird, bash also returns a status
716
717 shopt xpg_echo >/dev/null
718 echo noflag=$?
719
720 shopt -o errexit >/dev/null
721 echo set=$?
722
723 ## STDOUT:
724 shopt -u xpg_echo
725 q=1
726 shopt -s xpg_echo
727 shopt -u xpg_echo
728 p=1
729 noflag=1
730 set=1
731 ## END
732
733 ## N-I dash/mksh STDOUT:
734 ## END
735
736 #### shopt -p exit code (regression)
737 case $SH in dash|mksh) exit ;; esac
738
739 shopt -p > /dev/null
740 echo status=$?
741
742 ## STDOUT:
743 status=0
744 ## END
745
746 ## N-I dash/mksh STDOUT:
747 ## END
748
749 #### no-ops not shown by shopt -p
750
751 shopt -p | grep xpg
752 echo --
753 ## STDOUT:
754 --
755 ## END
756 ## OK bash STDOUT:
757 shopt -u xpg_echo
758 --
759 ## END
760
761