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