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
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 #### can continue after unknown option
70 # dash and mksh make this a fatal error no matter what.
71 set -o errexit
72 set -o STRICT || true # unknown option
73 echo 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
81 set -o errexit a b c
82 echo "$@"
83 false
84 echo done
85 ## stdout: a b c
86 ## status: 1
87
88 #### set -o vi/emacs
89 set -o vi
90 echo $?
91 set -o emacs
92 echo $?
93 ## STDOUT:
94 0
95 0
96 ## END
97
98 #### vi and emacs are mutually exclusive
99 show() {
100 shopt -o -p | egrep 'emacs$|vi$'
101 echo ___
102 };
103 show
104
105 set -o emacs
106 show
107
108 set -o vi
109 show
110
111 ## STDOUT:
112 set +o emacs
113 set +o vi
114 ___
115 set -o emacs
116 set +o vi
117 ___
118 set +o emacs
119 set -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
129 case $SH in (dash) exit ;; esac
130 case $SH in (bash|*osh) flag='--rcfile /dev/null' ;; esac
131
132 code='test -o emacs; echo $?; test -o vi; echo $?'
133
134 echo non-interactive
135 $SH $flag -c "$code"
136
137 echo interactive
138 $SH $flag -i -c "$code"
139
140 ## STDOUT:
141 non-interactive
142 1
143 1
144 interactive
145 0
146 1
147 ## END
148 ## OK mksh STDOUT:
149 non-interactive
150 0
151 1
152 interactive
153 0
154 1
155 ## END
156 ## N-I dash stdout-json: ""
157
158 #### nounset
159 echo "[$unset]"
160 set -o nounset
161 echo "[$unset]"
162 echo end # never reached
163 ## stdout: []
164 ## status: 1
165 ## OK dash status: 2
166
167 #### -u is nounset
168 echo "[$unset]"
169 set -u
170 echo "[$unset]"
171 echo end # never reached
172 ## stdout: []
173 ## status: 1
174 ## OK dash status: 2
175
176 #### nounset with "$@"
177 set a b c
178 set -u # shouldn't touch argv
179 echo "$@"
180 ## stdout: a b c
181
182 #### set -u -- clears argv
183 set a b c
184 set -u -- # shouldn't touch argv
185 echo "$@"
186 ## stdout:
187
188 #### set -u -- x y z
189 set a b c
190 set -u -- x y z
191 echo "$@"
192 ## stdout: x y z
193
194 #### reset option with long flag
195 set -o errexit
196 set +o errexit
197 echo "[$unset]"
198 ## stdout: []
199 ## status: 0
200
201 #### reset option with short flag
202 set -u
203 set +u
204 echo "[$unset]"
205 ## stdout: []
206 ## status: 0
207
208 #### set -eu (flag parsing)
209 set -eu
210 echo "[$unset]"
211 echo 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!
218 echo 1
219 set -n
220 echo 2
221 set +n
222 echo 3
223 # osh doesn't work because it only checks -n in bin/oil.py?
224 ## STDOUT:
225 1
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; }
233 echo $?
234 set -o pipefail
235 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
236 echo $?
237 ## STDOUT:
238 0
239 2
240 ## END
241 ## status: 0
242 ## N-I dash STDOUT:
243 0
244 ## END
245 ## N-I dash status: 2
246
247 #### shopt -p -o prints 'set' options
248 shopt -po nounset
249 set -o nounset
250 shopt -po nounset
251 ## STDOUT:
252 set +o nounset
253 set -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
259 shopt -p nullglob
260 shopt -s nullglob
261 shopt -p nullglob
262 ## STDOUT:
263 shopt -u nullglob
264 shopt -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
270 cd $TMP
271
272 # print specific options. OSH does it in a different format.
273 shopt nullglob failglob > one.txt
274 wc -l one.txt
275 grep -o nullglob one.txt
276 grep -o failglob one.txt
277
278 # print all options
279 shopt | grep nullglob | wc -l
280 ## STDOUT:
281 2 one.txt
282 nullglob
283 failglob
284 1
285 ## END
286 ## N-I dash/mksh STDOUT:
287 0 one.txt
288 0
289 ## END
290
291 #### noclobber off
292 set -o errexit
293 echo foo > $TMP/can-clobber
294 set +C
295 echo foo > $TMP/can-clobber
296 set +o noclobber
297 echo foo > $TMP/can-clobber
298 cat $TMP/can-clobber
299 ## stdout: foo
300
301 #### noclobber on
302 # Not implemented yet.
303 rm $TMP/no-clobber
304 set -C
305 echo foo > $TMP/no-clobber
306 echo $?
307 echo foo > $TMP/no-clobber
308 echo $?
309 ## stdout-json: "0\n1\n"
310 ## OK dash stdout-json: "0\n2\n"
311
312 #### SHELLOPTS is updated when options are changed
313 echo $SHELLOPTS | grep -q xtrace
314 echo $?
315 set -x
316 echo $SHELLOPTS | grep -q xtrace
317 echo $?
318 set +x
319 echo $SHELLOPTS | grep -q xtrace
320 echo $?
321 ## stdout-json: "1\n0\n1\n"
322 ## N-I dash/mksh stdout-json: "1\n1\n1\n"
323
324 #### SHELLOPTS is readonly
325 SHELLOPTS=x
326 echo 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 - -
335 set a b
336 echo "$@"
337 set - a b
338 echo "$@"
339 set -- a b
340 echo "$@"
341 set - -
342 echo "$@"
343 set - +
344 echo "$@"
345 set + -
346 echo "$@"
347 set -- --
348 echo "$@"
349
350 # note: zsh is different, and yash is totally different
351 ## STDOUT:
352 a b
353 a b
354 a b
355 -
356 +
357 +
358 --
359 ## END
360 ## OK osh/yash STDOUT:
361 a b
362 - a b
363 a b
364 - -
365 - +
366 + -
367 --
368 ## END
369 ## BUG mksh STDOUT:
370 a b
371 a b
372 a b
373 -
374 +
375 -
376 --
377 ## END
378 ## BUG zsh STDOUT:
379 a b
380 a b
381 a b
382
383 +
384
385 --
386 ## END
387
388 #### set -o lists options
389 # NOTE: osh doesn't use the same format yet.
390 set -o | grep -o noexec
391 ## STDOUT:
392 noexec
393 ## END
394
395 #### set without args lists variables
396 __GLOBAL=g
397 f() {
398 local __mylocal=L
399 local __OTHERLOCAL=L
400 __GLOBAL=mutated
401 set | grep '^__'
402 }
403 g() {
404 local __var_in_parent_scope=D
405 f
406 }
407 g
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
438 varfile=$TMP/vars-$(basename $SH).txt
439
440 set | grep '^_' > "$varfile"
441
442 # Unset variables
443 unset _space _whitespace _sq _backslash_dq _unicode
444 echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ]
445
446 # Restore them
447
448 . $varfile
449 echo "Code saved to $varfile" 1>&2 # for debugging
450
451 test "$_space" = '[ ]' && echo OK
452 test "$_whitespace" = $'[\t\r\n]' && echo OK
453 test "$_sq" = "'single quotes'" && echo OK
454 test "$_backslash_dq" = "\\ \"" && echo OK
455 test "$_unicode" = $'[\u03bc]' && echo OK
456
457 ## STDOUT:
458 [ ]
459 OK
460 OK
461 OK
462 OK
463 OK
464 ## END
465
466 #### set without args and array variables (not in OSH)
467 declare -a __array
468 __array=(1 2 '3 4')
469 set | 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)
483 typeset -A __assoc
484 __assoc['k e y']='v a l'
485 __assoc[a]=b
486 set | grep '^__'
487 ## STDOUT:
488 __assoc=(["k e y"]="v a l" [a]="b" )
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
498 shopt -q nullglob
499 echo nullglob=$?
500
501 # set it
502 shopt -s nullglob
503
504 shopt -q nullglob
505 echo nullglob=$?
506
507 shopt -q nullglob failglob
508 echo nullglob,failglob=$?
509
510 # set it
511 shopt -s failglob
512 shopt -q nullglob failglob
513 echo nullglob,failglob=$?
514
515 ## STDOUT:
516 nullglob=1
517 nullglob=0
518 nullglob,failglob=1
519 nullglob,failglob=0
520 ## END
521 ## N-I dash/mksh STDOUT:
522 nullglob=127
523 nullglob=127
524 nullglob,failglob=127
525 nullglob,failglob=127
526 ## END
527
528 #### shopt -q invalid
529 shopt -q invalidZZ
530 echo invalidZZ=$?
531 ## STDOUT:
532 invalidZZ=2
533 ## END
534 ## OK bash STDOUT:
535 invalidZZ=1
536 ## END
537 ## N-I dash/mksh STDOUT:
538 invalidZZ=127
539 ## END
540
541 #### shopt -s strict:all
542 n=2
543
544 show-strict() {
545 shopt -p | grep 'strict_' | head -n $n
546 echo -
547 }
548
549 show-strict
550 shopt -s strict:all
551 show-strict
552 shopt -u strict_arith
553 show-strict
554 ## STDOUT:
555 shopt -u strict_argv
556 shopt -u strict_arith
557 -
558 shopt -s strict_argv
559 shopt -s strict_arith
560 -
561 shopt -s strict_argv
562 shopt -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
576 set -o errexit
577
578 shopt -p nullglob || true # bash returns 1 here? Like -q.
579
580 # This should set nullglob, and return 1, which can be ignored
581 shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
582 echo status=$?
583
584 shopt -p nullglob || true
585
586 ## STDOUT:
587 shopt -u nullglob
588 status=0
589 shopt -s nullglob
590 ## END
591 ## N-I dash/mksh STDOUT:
592 status=0
593 ## END
594 ## N-I dash/mksh status: 0
595
596 #### shopt -p validates option names
597 shopt -p nullglob invalid failglob
598 echo status=$?
599 # same thing as -p, slightly different format in bash
600 shopt nullglob invalid failglob > $TMP/out.txt
601 status=$?
602 sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
603 echo status=$status
604 ## STDOUT:
605 status=2
606 status=2
607 ## END
608 ## OK bash STDOUT:
609 shopt -u nullglob
610 shopt -u failglob
611 status=1
612 nullglob off
613 failglob off
614 status=1
615 ## END
616 ## N-I dash/mksh STDOUT:
617 status=127
618 status=127
619 ## END
620
621 #### shopt -p -o validates option names
622 shopt -p -o errexit invalid nounset
623 echo status=$?
624 ## STDOUT:
625 set +o errexit
626 status=2
627 ## END
628 ## OK bash STDOUT:
629 set +o errexit
630 set +o nounset
631 status=1
632 ## END
633 ## N-I dash/mksh STDOUT:
634 status=127
635 ## END
636
637 #### stubbed out bash options
638 for name in foo autocd cdable_vars checkwinsize; do
639 shopt -s $name
640 echo $?
641 done
642 ## STDOUT:
643 2
644 0
645 0
646 0
647 ## END
648 ## OK bash STDOUT:
649 1
650 0
651 0
652 0
653 ## END
654 ## OK dash/mksh STDOUT:
655 127
656 127
657 127
658 127
659 ## END
660
661 #### shopt -s nounset works in Oil, not in bash
662 case $SH in
663 *dash|*mksh)
664 echo N-I
665 exit
666 ;;
667 esac
668 shopt -s nounset
669 echo status=$?
670
671 # get rid of extra space in bash output
672 set -o | grep nounset | sed 's/[ \t]\+/ /g'
673
674 ## STDOUT:
675 status=0
676 set -o nounset
677 ## END
678 ## OK bash STDOUT:
679 status=1
680 nounset off
681 # END
682 ## N-I dash/mksh STDOUT:
683 N-I
684 ## END
685
686 #### no-ops not in shopt -p output
687 shopt -p | grep xpg
688 echo --
689 ## STDOUT:
690 --
691 ## END
692 ## OK bash STDOUT:
693 shopt -u xpg_echo
694 --
695 ## END
696
697