1 |
# Cases relevant to Oil: |
2 |
# |
3 |
# - shopt -s more_errexit |
4 |
# - and maybe inherit_errexit and strict_errexit (OSH) |
5 |
# |
6 |
# Summary: |
7 |
# - errexit is reset to false in ash/bash -- completely ignored! |
8 |
# - local assignment is different than global! The exit code and errexit |
9 |
# behavior are different because the concept of the "last command" is |
10 |
# different. |
11 |
# - ash has copied bash behavior! |
12 |
|
13 |
#### command sub: errexit is NOT inherited and outer shell keeps going |
14 |
|
15 |
# This is the bash-specific bug here: |
16 |
# https://blogs.janestreet.com/when-bash-scripts-bite/ |
17 |
# See inherit_errexit below. |
18 |
# |
19 |
# I remember finding a script that relies on bash's bad behavior, so OSH copies |
20 |
# it. Instead more_errexit, is recommended. |
21 |
|
22 |
set -o errexit |
23 |
echo $(echo one; false; echo two) # bash/ash keep going |
24 |
echo parent status=$? |
25 |
## STDOUT: |
26 |
one two |
27 |
parent status=0 |
28 |
## END |
29 |
# dash and mksh: inner shell aborts, but outer one keeps going! |
30 |
## OK dash/mksh STDOUT: |
31 |
one |
32 |
parent status=0 |
33 |
## END |
34 |
|
35 |
#### command sub with inherit_errexit only |
36 |
set -o errexit |
37 |
shopt -s inherit_errexit || true |
38 |
echo zero |
39 |
echo $(echo one; false; echo two) # bash/ash keep going |
40 |
echo parent status=$? |
41 |
## STDOUT: |
42 |
zero |
43 |
one |
44 |
parent status=0 |
45 |
## END |
46 |
## N-I ash STDOUT: |
47 |
zero |
48 |
one two |
49 |
parent status=0 |
50 |
## END |
51 |
|
52 |
#### strict_errexit with command sub stops program |
53 |
set -o errexit |
54 |
shopt -s inherit_errexit || true |
55 |
shopt -s strict_errexit || true |
56 |
if echo $( echo 1; false; echo 2); then |
57 |
echo A |
58 |
fi |
59 |
echo done |
60 |
|
61 |
## status: 1 |
62 |
## stdout-json: "" |
63 |
|
64 |
## N-I bash/ash status: 0 |
65 |
## N-I bash/ash STDOUT: |
66 |
1 2 |
67 |
A |
68 |
done |
69 |
## END |
70 |
|
71 |
## N-I dash/mksh status: 0 |
72 |
## N-I dash/mksh STDOUT: |
73 |
1 |
74 |
A |
75 |
done |
76 |
## END |
77 |
|
78 |
#### {inherit,strict}_errexit: command sub with a single command |
79 |
set -o errexit |
80 |
shopt -s inherit_errexit || true |
81 |
shopt -s strict_errexit || true |
82 |
if echo $(false); then |
83 |
echo A |
84 |
fi |
85 |
echo done |
86 |
## status: 1 |
87 |
## stdout-json: "" |
88 |
|
89 |
## N-I dash/bash/mksh/ash status: 0 |
90 |
## N-I dash/bash/mksh/ash STDOUT: |
91 |
|
92 |
A |
93 |
done |
94 |
## END |
95 |
|
96 |
|
97 |
#### command sub with more_errexit only |
98 |
set -o errexit |
99 |
shopt -s more_errexit || true |
100 |
echo zero |
101 |
echo $(echo one; false; echo two) # bash/ash keep going |
102 |
echo parent status=$? |
103 |
## STDOUT: |
104 |
zero |
105 |
one two |
106 |
parent status=0 |
107 |
## END |
108 |
## N-I dash/mksh STDOUT: |
109 |
zero |
110 |
one |
111 |
parent status=0 |
112 |
## END |
113 |
|
114 |
#### command sub with inherit_errexit and more_errexit |
115 |
set -o errexit |
116 |
|
117 |
# bash implements inherit_errexit, but it's not as strict as OSH. |
118 |
shopt -s inherit_errexit || true |
119 |
shopt -s more_errexit || true |
120 |
echo zero |
121 |
echo $(echo one; false; echo two) # bash/ash keep going |
122 |
echo parent status=$? |
123 |
## STDOUT: |
124 |
zero |
125 |
## END |
126 |
## status: 1 |
127 |
## N-I dash/mksh/bash status: 0 |
128 |
## N-I dash/mksh/bash STDOUT: |
129 |
zero |
130 |
one |
131 |
parent status=0 |
132 |
## END |
133 |
## N-I ash status: 0 |
134 |
## N-I ash STDOUT: |
135 |
zero |
136 |
one two |
137 |
parent status=0 |
138 |
## END |
139 |
|
140 |
#### command sub: last command fails but keeps going and exit code is 0 |
141 |
set -o errexit |
142 |
echo $(echo one; false) # we lost the exit code |
143 |
echo status=$? |
144 |
## STDOUT: |
145 |
one |
146 |
status=0 |
147 |
## END |
148 |
|
149 |
#### global assignment with command sub: middle command fails |
150 |
set -o errexit |
151 |
s=$(echo one; false; echo two;) |
152 |
echo "$s" |
153 |
## status: 0 |
154 |
## STDOUT: |
155 |
one |
156 |
two |
157 |
## END |
158 |
# dash and mksh: whole thing aborts! |
159 |
## OK dash/mksh stdout-json: "" |
160 |
## OK dash/mksh status: 1 |
161 |
|
162 |
#### global assignment with command sub: last command fails and it aborts |
163 |
set -o errexit |
164 |
s=$(echo one; false) |
165 |
echo status=$? |
166 |
## stdout-json: "" |
167 |
## status: 1 |
168 |
|
169 |
#### local: middle command fails and keeps going |
170 |
set -o errexit |
171 |
f() { |
172 |
echo good |
173 |
local x=$(echo one; false; echo two) |
174 |
echo status=$? |
175 |
echo $x |
176 |
} |
177 |
f |
178 |
## STDOUT: |
179 |
good |
180 |
status=0 |
181 |
one two |
182 |
## END |
183 |
# for dash and mksh, the INNER shell aborts, but the outer one keeps going! |
184 |
## OK dash/mksh STDOUT: |
185 |
good |
186 |
status=0 |
187 |
one |
188 |
## END |
189 |
|
190 |
#### local: last command fails and also keeps going |
191 |
set -o errexit |
192 |
f() { |
193 |
echo good |
194 |
local x=$(echo one; false) |
195 |
echo status=$? |
196 |
echo $x |
197 |
} |
198 |
f |
199 |
## STDOUT: |
200 |
good |
201 |
status=0 |
202 |
one |
203 |
## END |
204 |
|
205 |
#### local and inherit_errexit / more_errexit |
206 |
# I've run into this problem a lot. |
207 |
set -o errexit |
208 |
shopt -s inherit_errexit || true # bash option |
209 |
shopt -s more_errexit || true # oil option |
210 |
f() { |
211 |
echo good |
212 |
local x=$(echo one; false; echo two) |
213 |
echo status=$? |
214 |
echo $x |
215 |
} |
216 |
f |
217 |
## status: 1 |
218 |
## STDOUT: |
219 |
good |
220 |
## END |
221 |
## N-I ash status: 0 |
222 |
## N-I ash STDOUT: |
223 |
good |
224 |
status=0 |
225 |
one two |
226 |
## END |
227 |
## N-I bash/dash/mksh status: 0 |
228 |
## N-I bash/dash/mksh STDOUT: |
229 |
good |
230 |
status=0 |
231 |
one |
232 |
## END |
233 |
|
234 |
#### global assignment when last status is failure |
235 |
# this is a bug I introduced |
236 |
set -o errexit |
237 |
[ -n "${BUILDDIR+x}" ] && _BUILDDIR=$BUILDDIR |
238 |
BUILDDIR=${_BUILDDIR-$BUILDDIR} |
239 |
echo status=$? |
240 |
## STDOUT: |
241 |
status=0 |
242 |
## END |
243 |
|
244 |
#### global assignment when last status is failure |
245 |
# this is a bug I introduced |
246 |
set -o errexit |
247 |
x=$(false) || true # from abuild |
248 |
[ -n "$APORTSDIR" ] && true |
249 |
BUILDDIR=${_BUILDDIR-$BUILDDIR} |
250 |
echo status=$? |
251 |
## STDOUT: |
252 |
status=0 |
253 |
## END |
254 |
|
255 |
#### strict_errexit prevents errexit from being disabled in function |
256 |
set -o errexit |
257 |
fun() { echo fun; } |
258 |
|
259 |
fun || true # this is OK |
260 |
|
261 |
shopt -s strict_errexit || true |
262 |
|
263 |
echo 'builtin ok' || true |
264 |
env echo 'external ok' || true |
265 |
|
266 |
fun || true # this fails |
267 |
|
268 |
## status: 1 |
269 |
## STDOUT: |
270 |
fun |
271 |
builtin ok |
272 |
external ok |
273 |
## END |
274 |
## N-I dash/bash/mksh/ash status: 0 |
275 |
## N-I dash/bash/mksh/ash STDOUT: |
276 |
fun |
277 |
builtin ok |
278 |
external ok |
279 |
fun |
280 |
## END |
281 |
|
282 |
#### strict_errexit prevents errexit from being disabled in brace group |
283 |
set -o errexit |
284 |
# false failure is NOT respected either way |
285 |
{ echo foo; false; echo bar; } || echo "failed" |
286 |
|
287 |
shopt -s strict_errexit || true |
288 |
{ echo foo; false; echo bar; } || echo "failed" |
289 |
## status: 1 |
290 |
## STDOUT: |
291 |
foo |
292 |
bar |
293 |
## END |
294 |
|
295 |
## N-I dash/bash/mksh/ash status: 0 |
296 |
## N-I dash/bash/mksh/ash STDOUT: |
297 |
foo |
298 |
bar |
299 |
foo |
300 |
bar |
301 |
## END |
302 |
|
303 |
#### strict_errexit prevents errexit from being disabled in subshell |
304 |
set -o errexit |
305 |
shopt -s inherit_errexit || true |
306 |
|
307 |
# false failure is NOT respected either way |
308 |
( echo foo; false; echo bar; ) || echo "failed" |
309 |
|
310 |
shopt -s strict_errexit || true |
311 |
( echo foo; false; echo bar; ) || echo "failed" |
312 |
## status: 1 |
313 |
## STDOUT: |
314 |
foo |
315 |
bar |
316 |
## END |
317 |
|
318 |
## N-I dash/bash/mksh/ash status: 0 |
319 |
## N-I dash/bash/mksh/ash STDOUT: |
320 |
foo |
321 |
bar |
322 |
foo |
323 |
bar |
324 |
## END |
325 |
|
326 |
#### strict_errexit and ! && || if while until |
327 |
prelude='set -o errexit |
328 |
shopt -s strict_errexit || true |
329 |
fun() { echo fun; }' |
330 |
|
331 |
$SH -c "$prelude; ! fun; echo 'should not get here'" |
332 |
echo bang=$? |
333 |
echo -- |
334 |
|
335 |
$SH -c "$prelude; fun || true" |
336 |
echo or=$? |
337 |
echo -- |
338 |
|
339 |
$SH -c "$prelude; fun && true" |
340 |
echo and=$? |
341 |
echo -- |
342 |
|
343 |
$SH -c "$prelude; if fun; then true; fi" |
344 |
echo if=$? |
345 |
echo -- |
346 |
|
347 |
$SH -c "$prelude; while fun; do echo while; exit; done" |
348 |
echo while=$? |
349 |
echo -- |
350 |
|
351 |
$SH -c "$prelude; until fun; do echo until; exit; done" |
352 |
echo until=$? |
353 |
echo -- |
354 |
|
355 |
|
356 |
## STDOUT: |
357 |
bang=1 |
358 |
-- |
359 |
or=1 |
360 |
-- |
361 |
and=1 |
362 |
-- |
363 |
if=1 |
364 |
-- |
365 |
while=1 |
366 |
-- |
367 |
until=1 |
368 |
-- |
369 |
## END |
370 |
## N-I dash/bash/mksh/ash STDOUT: |
371 |
fun |
372 |
should not get here |
373 |
bang=0 |
374 |
-- |
375 |
fun |
376 |
or=0 |
377 |
-- |
378 |
fun |
379 |
and=0 |
380 |
-- |
381 |
fun |
382 |
if=0 |
383 |
-- |
384 |
fun |
385 |
while |
386 |
while=0 |
387 |
-- |
388 |
fun |
389 |
until=0 |
390 |
-- |
391 |
## END |
392 |
|
393 |
#### if pipeline doesn't fail fatally |
394 |
set -o errexit |
395 |
set -o pipefail |
396 |
|
397 |
f() { |
398 |
local dir=$1 |
399 |
if ls $dir | grep ''; then |
400 |
echo foo |
401 |
echo ${PIPESTATUS[@]} |
402 |
fi |
403 |
} |
404 |
rmdir $TMP/_tmp || true |
405 |
rm -f $TMP/* |
406 |
f $TMP |
407 |
f /nonexistent # should fail |
408 |
echo done |
409 |
|
410 |
## N-I dash status: 2 |
411 |
## N-I dash stdout-json: "" |
412 |
## STDOUT: |
413 |
done |
414 |
## END |
415 |
|
416 |
#### if pipeline DOES fail fatally with strict_errexit |
417 |
set -o errexit |
418 |
set -o pipefail |
419 |
shopt -s strict_errexit || true |
420 |
|
421 |
# This "PIPELINE" is OK |
422 |
if ! false; then |
423 |
echo '! false ok' |
424 |
fi |
425 |
|
426 |
f() { |
427 |
local dir=$1 |
428 |
if ls $dir | grep ''; then |
429 |
echo foo |
430 |
fi |
431 |
} |
432 |
rmdir $TMP/_tmp || true |
433 |
rm -f $TMP/* |
434 |
f $TMP |
435 |
f /nonexistent # should fail |
436 |
echo done |
437 |
## status: 1 |
438 |
## STDOUT: |
439 |
! false ok |
440 |
## END |
441 |
## N-I bash/mksh/ash status: 0 |
442 |
## N-I bash/mksh/ash STDOUT: |
443 |
! false ok |
444 |
done |
445 |
## END |
446 |
## N-I dash status: 2 |
447 |
## N-I dash stdout-json: "" |
448 |
|
449 |
#### errexit is silent (verbose_errexit for Oil) |
450 |
shopt -u verbose_errexit 2>/dev/null || true |
451 |
set -e |
452 |
false |
453 |
## stderr-json: "" |
454 |
## status: 1 |