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