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: ""