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 |
#### nounset |
81 |
echo "[$unset]" |
82 |
set -o nounset |
83 |
echo "[$unset]" |
84 |
echo end # never reached |
85 |
## stdout: [] |
86 |
## status: 1 |
87 |
## OK dash status: 2 |
88 |
|
89 |
#### -u is nounset |
90 |
echo "[$unset]" |
91 |
set -u |
92 |
echo "[$unset]" |
93 |
echo end # never reached |
94 |
## stdout: [] |
95 |
## status: 1 |
96 |
## OK dash status: 2 |
97 |
|
98 |
#### nounset with "$@" |
99 |
set a b c |
100 |
set -u # shouldn't touch argv |
101 |
echo "$@" |
102 |
## stdout: a b c |
103 |
|
104 |
#### set -u -- clears argv |
105 |
set a b c |
106 |
set -u -- # shouldn't touch argv |
107 |
echo "$@" |
108 |
## stdout: |
109 |
|
110 |
#### set -u -- x y z |
111 |
set a b c |
112 |
set -u -- x y z |
113 |
echo "$@" |
114 |
## stdout: x y z |
115 |
|
116 |
#### reset option with long flag |
117 |
set -o errexit |
118 |
set +o errexit |
119 |
echo "[$unset]" |
120 |
## stdout: [] |
121 |
## status: 0 |
122 |
|
123 |
#### reset option with short flag |
124 |
set -u |
125 |
set +u |
126 |
echo "[$unset]" |
127 |
## stdout: [] |
128 |
## status: 0 |
129 |
|
130 |
#### set -eu (flag parsing) |
131 |
set -eu |
132 |
echo "[$unset]" |
133 |
echo status=$? |
134 |
## stdout-json: "" |
135 |
## status: 1 |
136 |
## OK dash status: 2 |
137 |
|
138 |
#### -n for no execution (useful with --ast-output) |
139 |
# NOTE: set +n doesn't work because nothing is executed! |
140 |
echo 1 |
141 |
set -n |
142 |
echo 2 |
143 |
set +n |
144 |
echo 3 |
145 |
# osh doesn't work because it only checks -n in bin/oil.py? |
146 |
## STDOUT: |
147 |
1 |
148 |
## END |
149 |
## status: 0 |
150 |
|
151 |
#### pipefail |
152 |
# NOTE: the sleeps are because osh can fail non-deterministically because of a |
153 |
# bug. Same problem as PIPESTATUS. |
154 |
{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; } |
155 |
echo $? |
156 |
set -o pipefail |
157 |
{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; } |
158 |
echo $? |
159 |
## STDOUT: |
160 |
0 |
161 |
2 |
162 |
## END |
163 |
## status: 0 |
164 |
## N-I dash STDOUT: |
165 |
0 |
166 |
## END |
167 |
## N-I dash status: 2 |
168 |
|
169 |
#### shopt -p -o prints 'set' options |
170 |
shopt -po nounset |
171 |
set -o nounset |
172 |
shopt -po nounset |
173 |
## STDOUT: |
174 |
set +o nounset |
175 |
set -o nounset |
176 |
## END |
177 |
## N-I dash/mksh stdout-json: "" |
178 |
## N-I dash/mksh status: 127 |
179 |
|
180 |
#### shopt -p prints 'shopt' options |
181 |
shopt -p nullglob |
182 |
shopt -s nullglob |
183 |
shopt -p nullglob |
184 |
## STDOUT: |
185 |
shopt -u nullglob |
186 |
shopt -s nullglob |
187 |
## END |
188 |
## N-I dash/mksh stdout-json: "" |
189 |
## N-I dash/mksh status: 127 |
190 |
|
191 |
#### shopt with no flags prints options |
192 |
cd $TMP |
193 |
|
194 |
# print specific options. OSH does it in a different format. |
195 |
shopt nullglob failglob > one.txt |
196 |
wc -l one.txt |
197 |
grep -o nullglob one.txt |
198 |
grep -o failglob one.txt |
199 |
|
200 |
# print all options |
201 |
shopt | grep nullglob | wc -l |
202 |
## STDOUT: |
203 |
2 one.txt |
204 |
nullglob |
205 |
failglob |
206 |
1 |
207 |
## END |
208 |
## N-I dash/mksh STDOUT: |
209 |
0 one.txt |
210 |
0 |
211 |
## END |
212 |
|
213 |
#### noclobber off |
214 |
set -o errexit |
215 |
echo foo > $TMP/can-clobber |
216 |
set +C |
217 |
echo foo > $TMP/can-clobber |
218 |
set +o noclobber |
219 |
echo foo > $TMP/can-clobber |
220 |
cat $TMP/can-clobber |
221 |
## stdout: foo |
222 |
|
223 |
#### noclobber on |
224 |
# Not implemented yet. |
225 |
rm $TMP/no-clobber |
226 |
set -C |
227 |
echo foo > $TMP/no-clobber |
228 |
echo $? |
229 |
echo foo > $TMP/no-clobber |
230 |
echo $? |
231 |
## stdout-json: "0\n1\n" |
232 |
## OK dash stdout-json: "0\n2\n" |
233 |
|
234 |
#### SHELLOPTS is updated when options are changed |
235 |
echo $SHELLOPTS | grep -q xtrace |
236 |
echo $? |
237 |
set -x |
238 |
echo $SHELLOPTS | grep -q xtrace |
239 |
echo $? |
240 |
set +x |
241 |
echo $SHELLOPTS | grep -q xtrace |
242 |
echo $? |
243 |
## stdout-json: "1\n0\n1\n" |
244 |
## N-I dash/mksh stdout-json: "1\n1\n1\n" |
245 |
|
246 |
#### SHELLOPTS is readonly |
247 |
SHELLOPTS=x |
248 |
echo status=$? |
249 |
## stdout: status=1 |
250 |
## N-I dash/mksh stdout: status=0 |
251 |
|
252 |
# Setting a readonly variable in osh is a hard failure. |
253 |
## OK osh status: 1 |
254 |
## OK osh stdout-json: "" |
255 |
|
256 |
#### set -o lists options |
257 |
# NOTE: osh doesn't use the same format yet. |
258 |
set -o | grep -o noexec |
259 |
## STDOUT: |
260 |
noexec |
261 |
## END |
262 |
|
263 |
#### set without args lists variables |
264 |
__GLOBAL=g |
265 |
f() { |
266 |
local __mylocal=L |
267 |
local __OTHERLOCAL=L |
268 |
__GLOBAL=mutated |
269 |
set | grep '^__' |
270 |
} |
271 |
g() { |
272 |
local __var_in_parent_scope=D |
273 |
f |
274 |
} |
275 |
g |
276 |
## status: 0 |
277 |
## STDOUT: |
278 |
__GLOBAL='mutated' |
279 |
__OTHERLOCAL='L' |
280 |
__mylocal='L' |
281 |
__var_in_parent_scope='D' |
282 |
## END |
283 |
## OK bash STDOUT: |
284 |
__GLOBAL=mutated |
285 |
__OTHERLOCAL=L |
286 |
__mylocal=L |
287 |
__var_in_parent_scope=D |
288 |
## END |
289 |
## OK mksh STDOUT: |
290 |
__GLOBAL=mutated |
291 |
__var_in_parent_scope=D |
292 |
__OTHERLOCAL=L |
293 |
__mylocal=L |
294 |
## END |
295 |
|
296 |
#### 'set' and 'eval' round trip |
297 |
|
298 |
# NOTE: not testing arrays and associative arrays! |
299 |
_space='[ ]' |
300 |
_whitespace=$'[\t\r\n]' |
301 |
_sq="'single quotes'" |
302 |
_backslash_dq="\\ \"" |
303 |
_unicode=$'[\u03bc]' |
304 |
|
305 |
# Save the variables |
306 |
varfile=$TMP/vars-$(basename $SH).txt |
307 |
|
308 |
set | grep '^_' > "$varfile" |
309 |
|
310 |
# Unset variables |
311 |
unset _space _whitespace _sq _backslash_dq _unicode |
312 |
echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ] |
313 |
|
314 |
# Restore them |
315 |
|
316 |
. $varfile |
317 |
echo "Code saved to $varfile" 1>&2 # for debugging |
318 |
|
319 |
test "$_space" = '[ ]' && echo OK |
320 |
test "$_whitespace" = $'[\t\r\n]' && echo OK |
321 |
test "$_sq" = "'single quotes'" && echo OK |
322 |
test "$_backslash_dq" = "\\ \"" && echo OK |
323 |
test "$_unicode" = $'[\u03bc]' && echo OK |
324 |
|
325 |
## STDOUT: |
326 |
[ ] |
327 |
OK |
328 |
OK |
329 |
OK |
330 |
OK |
331 |
OK |
332 |
## END |
333 |
|
334 |
#### set without args and array variables (not in OSH) |
335 |
declare -a __array |
336 |
__array=(1 2 '3 4') |
337 |
set | grep '^__' |
338 |
## STDOUT: |
339 |
__array=([0]="1" [1]="2" [2]="3 4") |
340 |
## END |
341 |
## OK mksh STDOUT: |
342 |
__array[0]=1 |
343 |
__array[1]=2 |
344 |
__array[2]='3 4' |
345 |
## N-I dash stdout-json: "" |
346 |
## N-I dash status: 2 |
347 |
## N-I osh stdout-json: "" |
348 |
## N-I osh status: 1 |
349 |
|
350 |
#### set without args and assoc array variables (not in OSH) |
351 |
typeset -A __assoc |
352 |
__assoc['k e y']='v a l' |
353 |
__assoc[a]=b |
354 |
set | grep '^__' |
355 |
## STDOUT: |
356 |
__assoc=(["k e y"]="v a l" [a]="b" ) |
357 |
## END |
358 |
## N-I mksh stdout-json: "" |
359 |
## N-I mksh status: 1 |
360 |
## N-I dash stdout-json: "" |
361 |
## N-I dash status: 1 |
362 |
## N-I osh stdout-json: "" |
363 |
## N-I osh status: 1 |
364 |
|
365 |
#### shopt -q |
366 |
shopt -q nullglob |
367 |
echo nullglob=$? |
368 |
|
369 |
# set it |
370 |
shopt -s nullglob |
371 |
|
372 |
shopt -q nullglob |
373 |
echo nullglob=$? |
374 |
|
375 |
shopt -q nullglob failglob |
376 |
echo nullglob,failglob=$? |
377 |
|
378 |
# set it |
379 |
shopt -s failglob |
380 |
shopt -q nullglob failglob |
381 |
echo nullglob,failglob=$? |
382 |
|
383 |
## STDOUT: |
384 |
nullglob=1 |
385 |
nullglob=0 |
386 |
nullglob,failglob=1 |
387 |
nullglob,failglob=0 |
388 |
## END |
389 |
## N-I dash/mksh STDOUT: |
390 |
nullglob=127 |
391 |
nullglob=127 |
392 |
nullglob,failglob=127 |
393 |
nullglob,failglob=127 |
394 |
## END |
395 |
|
396 |
#### shopt -q invalid |
397 |
shopt -q invalidZZ |
398 |
echo invalidZZ=$? |
399 |
## STDOUT: |
400 |
invalidZZ=2 |
401 |
## END |
402 |
## OK bash STDOUT: |
403 |
invalidZZ=1 |
404 |
## END |
405 |
## N-I dash/mksh STDOUT: |
406 |
invalidZZ=127 |
407 |
## END |
408 |
|
409 |
#### shopt -s strict:all |
410 |
n=2 |
411 |
|
412 |
show-strict() { |
413 |
shopt -p | grep 'strict_' | head -n $n |
414 |
echo - |
415 |
} |
416 |
|
417 |
show-strict |
418 |
shopt -s strict:all |
419 |
show-strict |
420 |
shopt -u strict_arith |
421 |
show-strict |
422 |
## STDOUT: |
423 |
shopt -u strict_argv |
424 |
shopt -u strict_arith |
425 |
- |
426 |
shopt -s strict_argv |
427 |
shopt -s strict_arith |
428 |
- |
429 |
shopt -s strict_argv |
430 |
shopt -u strict_arith |
431 |
- |
432 |
## END |
433 |
## N-I dash status: 2 |
434 |
## N-I dash stdout-json: "" |
435 |
## N-I bash/mksh STDOUT: |
436 |
- |
437 |
- |
438 |
- |
439 |
## END |
440 |
|
441 |
#### shopt allows for backward compatibility like bash |
442 |
|
443 |
# doesn't have to be on, but just for testing |
444 |
set -o errexit |
445 |
|
446 |
shopt -p nullglob || true # bash returns 1 here? Like -q. |
447 |
|
448 |
# This should set nullglob, and return 1, which can be ignored |
449 |
shopt -s nullglob strict_OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true |
450 |
echo status=$? |
451 |
|
452 |
shopt -p nullglob || true |
453 |
|
454 |
## STDOUT: |
455 |
shopt -u nullglob |
456 |
status=0 |
457 |
shopt -s nullglob |
458 |
## END |
459 |
## N-I dash/mksh STDOUT: |
460 |
status=0 |
461 |
## END |
462 |
## N-I dash/mksh status: 0 |
463 |
|
464 |
#### shopt -p validates option names |
465 |
shopt -p nullglob invalid failglob |
466 |
echo status=$? |
467 |
# same thing as -p, slightly different format in bash |
468 |
shopt nullglob invalid failglob > $TMP/out.txt |
469 |
status=$? |
470 |
sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert |
471 |
echo status=$status |
472 |
## STDOUT: |
473 |
shopt -u nullglob |
474 |
status=2 |
475 |
shopt -u nullglob |
476 |
status=2 |
477 |
## END |
478 |
## OK bash STDOUT: |
479 |
shopt -u nullglob |
480 |
shopt -u failglob |
481 |
status=1 |
482 |
nullglob off |
483 |
failglob off |
484 |
status=1 |
485 |
## END |
486 |
## N-I dash/mksh STDOUT: |
487 |
status=127 |
488 |
status=127 |
489 |
## END |
490 |
|
491 |
#### shopt -p -o validates option names |
492 |
shopt -p -o errexit invalid nounset |
493 |
echo status=$? |
494 |
## STDOUT: |
495 |
set +o errexit |
496 |
status=2 |
497 |
## END |
498 |
## OK bash STDOUT: |
499 |
set +o errexit |
500 |
set +o nounset |
501 |
status=1 |
502 |
## END |
503 |
## N-I dash/mksh STDOUT: |
504 |
status=127 |
505 |
## END |