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 - - |
333 |
set a b |
334 |
echo "$@" |
335 |
set - a b |
336 |
echo "$@" |
337 |
set -- a b |
338 |
echo "$@" |
339 |
set - - |
340 |
echo "$@" |
341 |
set - + |
342 |
echo "$@" |
343 |
set + - |
344 |
echo "$@" |
345 |
set -- -- |
346 |
echo "$@" |
347 |
|
348 |
# note: zsh is diffferent, and yash is totally different |
349 |
## STDOUT: |
350 |
a b |
351 |
a b |
352 |
a b |
353 |
- |
354 |
+ |
355 |
+ |
356 |
-- |
357 |
## END |
358 |
## OK osh/yash STDOUT: |
359 |
a b |
360 |
- a b |
361 |
a b |
362 |
- - |
363 |
- + |
364 |
+ - |
365 |
-- |
366 |
## END |
367 |
## BUG mksh STDOUT: |
368 |
a b |
369 |
a b |
370 |
a b |
371 |
- |
372 |
+ |
373 |
- |
374 |
-- |
375 |
## END |
376 |
## BUG zsh STDOUT: |
377 |
a b |
378 |
a b |
379 |
a b |
380 |
|
381 |
+ |
382 |
|
383 |
-- |
384 |
## END |
385 |
|
386 |
#### set -o lists options |
387 |
# NOTE: osh doesn't use the same format yet. |
388 |
set -o | grep -o noexec |
389 |
## STDOUT: |
390 |
noexec |
391 |
## END |
392 |
|
393 |
#### set without args lists variables |
394 |
__GLOBAL=g |
395 |
f() { |
396 |
local __mylocal=L |
397 |
local __OTHERLOCAL=L |
398 |
__GLOBAL=mutated |
399 |
set | grep '^__' |
400 |
} |
401 |
g() { |
402 |
local __var_in_parent_scope=D |
403 |
f |
404 |
} |
405 |
g |
406 |
## status: 0 |
407 |
## STDOUT: |
408 |
__GLOBAL=mutated |
409 |
__OTHERLOCAL=L |
410 |
__mylocal=L |
411 |
__var_in_parent_scope=D |
412 |
## END |
413 |
## OK mksh STDOUT: |
414 |
__GLOBAL=mutated |
415 |
__var_in_parent_scope=D |
416 |
__OTHERLOCAL=L |
417 |
__mylocal=L |
418 |
## END |
419 |
## OK dash STDOUT: |
420 |
__GLOBAL='mutated' |
421 |
__OTHERLOCAL='L' |
422 |
__mylocal='L' |
423 |
__var_in_parent_scope='D' |
424 |
## END |
425 |
|
426 |
#### 'set' and 'eval' round trip |
427 |
|
428 |
# NOTE: not testing arrays and associative arrays! |
429 |
_space='[ ]' |
430 |
_whitespace=$'[\t\r\n]' |
431 |
_sq="'single quotes'" |
432 |
_backslash_dq="\\ \"" |
433 |
_unicode=$'[\u03bc]' |
434 |
|
435 |
# Save the variables |
436 |
varfile=$TMP/vars-$(basename $SH).txt |
437 |
|
438 |
set | grep '^_' > "$varfile" |
439 |
|
440 |
# Unset variables |
441 |
unset _space _whitespace _sq _backslash_dq _unicode |
442 |
echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ] |
443 |
|
444 |
# Restore them |
445 |
|
446 |
. $varfile |
447 |
echo "Code saved to $varfile" 1>&2 # for debugging |
448 |
|
449 |
test "$_space" = '[ ]' && echo OK |
450 |
test "$_whitespace" = $'[\t\r\n]' && echo OK |
451 |
test "$_sq" = "'single quotes'" && echo OK |
452 |
test "$_backslash_dq" = "\\ \"" && echo OK |
453 |
test "$_unicode" = $'[\u03bc]' && echo OK |
454 |
|
455 |
## STDOUT: |
456 |
[ ] |
457 |
OK |
458 |
OK |
459 |
OK |
460 |
OK |
461 |
OK |
462 |
## END |
463 |
|
464 |
#### set without args and array variables (not in OSH) |
465 |
declare -a __array |
466 |
__array=(1 2 '3 4') |
467 |
set | grep '^__' |
468 |
## STDOUT: |
469 |
__array=([0]="1" [1]="2" [2]="3 4") |
470 |
## END |
471 |
## OK mksh STDOUT: |
472 |
__array[0]=1 |
473 |
__array[1]=2 |
474 |
__array[2]='3 4' |
475 |
## N-I dash stdout-json: "" |
476 |
## N-I dash status: 2 |
477 |
## N-I osh stdout-json: "" |
478 |
## N-I osh status: 1 |
479 |
|
480 |
#### set without args and assoc array variables (not in OSH) |
481 |
typeset -A __assoc |
482 |
__assoc['k e y']='v a l' |
483 |
__assoc[a]=b |
484 |
set | grep '^__' |
485 |
## STDOUT: |
486 |
__assoc=(["k e y"]="v a l" [a]="b" ) |
487 |
## END |
488 |
## N-I mksh stdout-json: "" |
489 |
## N-I mksh status: 1 |
490 |
## N-I dash stdout-json: "" |
491 |
## N-I dash status: 1 |
492 |
## N-I osh stdout-json: "" |
493 |
## N-I osh status: 1 |
494 |
|
495 |
#### shopt -q |
496 |
shopt -q nullglob |
497 |
echo nullglob=$? |
498 |
|
499 |
# set it |
500 |
shopt -s nullglob |
501 |
|
502 |
shopt -q nullglob |
503 |
echo nullglob=$? |
504 |
|
505 |
shopt -q nullglob failglob |
506 |
echo nullglob,failglob=$? |
507 |
|
508 |
# set it |
509 |
shopt -s failglob |
510 |
shopt -q nullglob failglob |
511 |
echo nullglob,failglob=$? |
512 |
|
513 |
## STDOUT: |
514 |
nullglob=1 |
515 |
nullglob=0 |
516 |
nullglob,failglob=1 |
517 |
nullglob,failglob=0 |
518 |
## END |
519 |
## N-I dash/mksh STDOUT: |
520 |
nullglob=127 |
521 |
nullglob=127 |
522 |
nullglob,failglob=127 |
523 |
nullglob,failglob=127 |
524 |
## END |
525 |
|
526 |
#### shopt -q invalid |
527 |
shopt -q invalidZZ |
528 |
echo invalidZZ=$? |
529 |
## STDOUT: |
530 |
invalidZZ=2 |
531 |
## END |
532 |
## OK bash STDOUT: |
533 |
invalidZZ=1 |
534 |
## END |
535 |
## N-I dash/mksh STDOUT: |
536 |
invalidZZ=127 |
537 |
## END |
538 |
|
539 |
#### shopt -s strict:all |
540 |
n=2 |
541 |
|
542 |
show-strict() { |
543 |
shopt -p | grep 'strict_' | head -n $n |
544 |
echo - |
545 |
} |
546 |
|
547 |
show-strict |
548 |
shopt -s strict:all |
549 |
show-strict |
550 |
shopt -u strict_arith |
551 |
show-strict |
552 |
## STDOUT: |
553 |
shopt -u strict_argv |
554 |
shopt -u strict_arith |
555 |
- |
556 |
shopt -s strict_argv |
557 |
shopt -s strict_arith |
558 |
- |
559 |
shopt -s strict_argv |
560 |
shopt -u strict_arith |
561 |
- |
562 |
## END |
563 |
## N-I dash status: 2 |
564 |
## N-I dash stdout-json: "" |
565 |
## N-I bash/mksh STDOUT: |
566 |
- |
567 |
- |
568 |
- |
569 |
## END |
570 |
|
571 |
#### shopt allows for backward compatibility like bash |
572 |
|
573 |
# doesn't have to be on, but just for testing |
574 |
set -o errexit |
575 |
|
576 |
shopt -p nullglob || true # bash returns 1 here? Like -q. |
577 |
|
578 |
# This should set nullglob, and return 1, which can be ignored |
579 |
shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true |
580 |
echo status=$? |
581 |
|
582 |
shopt -p nullglob || true |
583 |
|
584 |
## STDOUT: |
585 |
shopt -u nullglob |
586 |
status=0 |
587 |
shopt -s nullglob |
588 |
## END |
589 |
## N-I dash/mksh STDOUT: |
590 |
status=0 |
591 |
## END |
592 |
## N-I dash/mksh status: 0 |
593 |
|
594 |
#### shopt -p validates option names |
595 |
shopt -p nullglob invalid failglob |
596 |
echo status=$? |
597 |
# same thing as -p, slightly different format in bash |
598 |
shopt nullglob invalid failglob > $TMP/out.txt |
599 |
status=$? |
600 |
sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert |
601 |
echo status=$status |
602 |
## STDOUT: |
603 |
shopt -u nullglob |
604 |
status=2 |
605 |
shopt -u nullglob |
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 doesn't work (may relax this later) |
662 |
case $SH in |
663 |
*dash|*mksh) |
664 |
echo N-I |
665 |
exit |
666 |
;; |
667 |
esac |
668 |
shopt -s nounset |
669 |
echo status=$? |
670 |
# get rid of extra space in bash |
671 |
set -o | grep nounset | sed 's/[ \t]\+/ /g' |
672 |
|
673 |
## STDOUT: |
674 |
status=2 |
675 |
set +o nounset |
676 |
## END |
677 |
## OK bash STDOUT: |
678 |
status=1 |
679 |
nounset off |
680 |
# END |
681 |
## N-I dash/mksh STDOUT: |
682 |
N-I |
683 |
## END |
684 |
|
685 |
#### no-ops not in shopt -p output |
686 |
shopt -p | grep xpg |
687 |
echo -- |
688 |
## STDOUT: |
689 |
-- |
690 |
## END |
691 |
## OK bash STDOUT: |
692 |
shopt -u xpg_echo |
693 |
-- |
694 |
## END |
695 |
|
696 |
|