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