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