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