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 #### set without args lists variables
324 __GLOBAL=g
325 f() {
326 local __mylocal=L
327 local __OTHERLOCAL=L
328 __GLOBAL=mutated
329 set | grep '^__'
330 }
331 g() {
332 local __var_in_parent_scope=D
333 f
334 }
335 g
336 ## status: 0
337 ## STDOUT:
338 __GLOBAL=mutated
339 __OTHERLOCAL=L
340 __mylocal=L
341 __var_in_parent_scope=D
342 ## END
343 ## OK mksh STDOUT:
344 __GLOBAL=mutated
345 __var_in_parent_scope=D
346 __OTHERLOCAL=L
347 __mylocal=L
348 ## END
349 ## OK dash STDOUT:
350 __GLOBAL='mutated'
351 __OTHERLOCAL='L'
352 __mylocal='L'
353 __var_in_parent_scope='D'
354 ## END
355
356 #### set without args and array variables
357 declare -a __array
358 __array=(1 2 '3 4')
359 set | grep '^__'
360 ## STDOUT:
361 __array=(1 2 '3 4')
362 ## END
363 ## OK bash STDOUT:
364 __array=([0]="1" [1]="2" [2]="3 4")
365 ## END
366 ## OK mksh STDOUT:
367 __array[0]=1
368 __array[1]=2
369 __array[2]='3 4'
370 ## END
371 ## OK zsh STDOUT:
372 a=( 1 2 3 )
373 ## END
374 ## N-I dash stdout-json: ""
375 ## N-I dash status: 2
376
377 #### set without args and assoc array variables (not in OSH)
378 typeset -A __assoc
379 __assoc['k e y']='v a l'
380 __assoc[a]=b
381 set | grep '^__'
382 ## STDOUT:
383 __assoc=([a]="b" ["k e y"]="v a l" )
384 ## END
385 ## N-I mksh stdout-json: ""
386 ## N-I mksh status: 1
387 ## N-I dash stdout-json: ""
388 ## N-I dash status: 1
389 ## N-I osh stdout-json: ""
390 ## N-I osh status: 1
391
392 #### shopt -q
393 shopt -q nullglob
394 echo nullglob=$?
395
396 # set it
397 shopt -s nullglob
398
399 shopt -q nullglob
400 echo nullglob=$?
401
402 shopt -q nullglob failglob
403 echo nullglob,failglob=$?
404
405 # set it
406 shopt -s failglob
407 shopt -q nullglob failglob
408 echo nullglob,failglob=$?
409
410 ## STDOUT:
411 nullglob=1
412 nullglob=0
413 nullglob,failglob=1
414 nullglob,failglob=0
415 ## END
416 ## N-I dash/mksh STDOUT:
417 nullglob=127
418 nullglob=127
419 nullglob,failglob=127
420 nullglob,failglob=127
421 ## END
422
423 #### shopt -q invalid
424 shopt -q invalidZZ
425 echo invalidZZ=$?
426 ## STDOUT:
427 invalidZZ=2
428 ## END
429 ## OK bash STDOUT:
430 invalidZZ=1
431 ## END
432 ## N-I dash/mksh STDOUT:
433 invalidZZ=127
434 ## END
435
436 #### shopt -s strict:all
437 n=2
438
439 show-strict() {
440 shopt -p | grep 'strict_' | head -n $n
441 echo -
442 }
443
444 show-strict
445 shopt -s strict:all
446 show-strict
447 shopt -u strict_arith
448 show-strict
449 ## STDOUT:
450 shopt -u strict_argv
451 shopt -u strict_arith
452 -
453 shopt -s strict_argv
454 shopt -s strict_arith
455 -
456 shopt -s strict_argv
457 shopt -u strict_arith
458 -
459 ## END
460 ## N-I dash status: 2
461 ## N-I dash stdout-json: ""
462 ## N-I bash/mksh STDOUT:
463 -
464 -
465 -
466 ## END
467
468 #### shopt allows for backward compatibility like bash
469
470 # doesn't have to be on, but just for testing
471 set -o errexit
472
473 shopt -p nullglob || true # bash returns 1 here? Like -q.
474
475 # This should set nullglob, and return 1, which can be ignored
476 shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
477 echo status=$?
478
479 shopt -p nullglob || true
480
481 ## STDOUT:
482 shopt -u nullglob
483 status=0
484 shopt -s nullglob
485 ## END
486 ## N-I dash/mksh STDOUT:
487 status=0
488 ## END
489 ## N-I dash/mksh status: 0
490
491 #### shopt -p validates option names
492 shopt -p nullglob invalid failglob
493 echo status=$?
494 # same thing as -p, slightly different format in bash
495 shopt nullglob invalid failglob > $TMP/out.txt
496 status=$?
497 sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
498 echo status=$status
499 ## STDOUT:
500 status=2
501 status=2
502 ## END
503 ## OK bash STDOUT:
504 shopt -u nullglob
505 shopt -u failglob
506 status=1
507 nullglob off
508 failglob off
509 status=1
510 ## END
511 ## N-I dash/mksh STDOUT:
512 status=127
513 status=127
514 ## END
515
516 #### shopt -p -o validates option names
517 shopt -p -o errexit invalid nounset
518 echo status=$?
519 ## STDOUT:
520 set +o errexit
521 status=2
522 ## END
523 ## OK bash STDOUT:
524 set +o errexit
525 set +o nounset
526 status=1
527 ## END
528 ## N-I dash/mksh STDOUT:
529 status=127
530 ## END
531
532 #### stubbed out bash options
533 shopt -s ignore_shopt_not_impl
534 for name in foo autocd cdable_vars checkwinsize; do
535 shopt -s $name
536 echo $?
537 done
538 ## STDOUT:
539 2
540 0
541 0
542 0
543 ## END
544 ## OK bash STDOUT:
545 1
546 0
547 0
548 0
549 ## END
550 ## OK dash/mksh STDOUT:
551 127
552 127
553 127
554 127
555 ## END
556
557 #### shopt -s nounset works in YSH, not in bash
558 case $SH in
559 *dash|*mksh)
560 echo N-I
561 exit
562 ;;
563 esac
564 shopt -s nounset
565 echo status=$?
566
567 # get rid of extra space in bash output
568 set -o | grep nounset | sed 's/[ \t]\+/ /g'
569
570 ## STDOUT:
571 status=0
572 set -o nounset
573 ## END
574 ## OK bash STDOUT:
575 status=1
576 nounset off
577 # END
578 ## N-I dash/mksh STDOUT:
579 N-I
580 ## END
581
582 #### Unimplemented options - print, query, set, unset
583 case $SH in dash|mksh) exit ;; esac
584
585 opt_name=xpg_echo
586
587 shopt -p xpg_echo
588 shopt -q xpg_echo; echo q=$?
589
590 shopt -s xpg_echo
591 shopt -p xpg_echo
592
593 shopt -u xpg_echo
594 shopt -p xpg_echo
595 echo p=$? # weird, bash also returns a status
596
597 shopt xpg_echo >/dev/null
598 echo noflag=$?
599
600 shopt -o errexit >/dev/null
601 echo set=$?
602
603 ## STDOUT:
604 q=2
605 p=2
606 noflag=2
607 set=1
608 ## END
609
610 ## OK bash STDOUT:
611 shopt -u xpg_echo
612 q=1
613 shopt -s xpg_echo
614 shopt -u xpg_echo
615 p=1
616 noflag=1
617 set=1
618 ## END
619
620 ## N-I dash/mksh STDOUT:
621 ## END
622
623 #### Unimplemented options - OSH shopt -s ignore_shopt_not_impl
624 case $SH in dash|mksh) exit ;; esac
625
626 shopt -s ignore_shopt_not_impl
627
628 opt_name=xpg_echo
629
630 shopt -p xpg_echo
631 shopt -q xpg_echo; echo q=$?
632
633 shopt -s xpg_echo
634 shopt -p xpg_echo
635
636 shopt -u xpg_echo
637 shopt -p xpg_echo
638 echo p=$? # weird, bash also returns a status
639
640 shopt xpg_echo >/dev/null
641 echo noflag=$?
642
643 shopt -o errexit >/dev/null
644 echo set=$?
645
646 ## STDOUT:
647 shopt -u xpg_echo
648 q=1
649 shopt -s xpg_echo
650 shopt -u xpg_echo
651 p=1
652 noflag=1
653 set=1
654 ## END
655
656 ## N-I dash/mksh STDOUT:
657 ## END
658
659 #### shopt -p exit code (regression)
660 case $SH in dash|mksh) exit ;; esac
661
662 shopt -p > /dev/null
663 echo status=$?
664
665 ## STDOUT:
666 status=0
667 ## END
668
669 ## N-I dash/mksh STDOUT:
670 ## END
671
672 #### no-ops not shown by shopt -p
673
674 shopt -p | grep xpg
675 echo --
676 ## STDOUT:
677 --
678 ## END
679 ## OK bash STDOUT:
680 shopt -u xpg_echo
681 --
682 ## END
683
684