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