1 ## compare_shells: dash bash mksh ash
2 ## oils_failures_allowed: 0
3
4 # TODO: upgrade to bash 5.2 and make OSH behave like that! redirect failures
5
6 #### errexit aborts early
7 set -o errexit
8 false
9 echo done
10 ## stdout-json: ""
11 ## status: 1
12
13 #### errexit for nonexistent command
14 set -o errexit
15 nonexistent__ZZ
16 echo done
17 ## stdout-json: ""
18 ## status: 127
19
20 #### errexit aborts early on pipeline
21 set -o errexit
22 echo hi | grep nonexistent
23 echo two
24 ## stdout-json: ""
25 ## status: 1
26
27 #### errexit with { }
28 # This aborts because it's not part of an if statement.
29 set -o errexit
30 { echo one; false; echo two; }
31 ## stdout: one
32 ## status: 1
33
34 #### errexit with if and { }
35 set -o errexit
36 if { echo one; false; echo two; }; then
37 echo three
38 fi
39 echo four
40 ## status: 0
41 ## STDOUT:
42 one
43 two
44 three
45 four
46 ## END
47
48 #### errexit with ||
49 set -o errexit
50 echo hi | grep nonexistent || echo ok
51 ## stdout: ok
52 ## status: 0
53
54 #### errexit with &&
55 set -o errexit
56 echo ok && echo hi | grep nonexistent
57 ## stdout: ok
58 ## status: 1
59
60 #### errexit test && -- from gen-module-init
61 set -o errexit
62 test "$mod" = readline && echo "#endif"
63 echo status=$?
64 ## stdout: status=1
65
66 #### errexit test && and fail
67 set -o errexit
68 test -n X && false
69 echo status=$?
70 ## stdout-json: ""
71 ## status: 1
72
73 #### errexit and loop
74 set -o errexit
75 for x in 1 2 3; do
76 test $x = 2 && echo "hi $x"
77 done
78 ## stdout: hi 2
79 ## status: 1
80
81 #### errexit and brace group { }
82 set -o errexit
83 { test no = yes && echo hi; }
84 echo status=$?
85 ## stdout: status=1
86
87 #### errexit and time { }
88 set -o errexit
89 time false
90 echo status=$?
91 ## status: 1
92
93 #### errexit with !
94 set -o errexit
95 echo one
96 ! true
97 echo two
98 ! false
99 echo three
100 ## STDOUT:
101 one
102 two
103 three
104 ## END
105
106 #### errexit with ! and ;
107 # AST has extra Sentence nodes; there was a REGRESSION here.
108 set -o errexit; echo one; ! true; echo two; ! false; echo three
109 ## STDOUT:
110 one
111 two
112 three
113 ## END
114
115 #### errexit with while/until
116 set -o errexit
117 while false; do
118 echo ok
119 done
120 until false; do
121 echo ok # do this once then exit loop
122 break
123 done
124 ## stdout: ok
125 ## status: 0
126
127 #### errexit with (( ))
128 # from http://mywiki.wooledge.org/BashFAQ/105, this changed between versions.
129 # ash says that 'i++' is not found, but it doesn't exit. I guess this is the
130 # subshell problem?
131 set -o errexit
132 i=0
133 (( i++ ))
134 echo done
135 ## stdout-json: ""
136 ## status: 1
137 ## N-I dash/ash status: 127
138 ## N-I dash/ash stdout-json: ""
139
140 #### errexit with subshell
141 set -o errexit
142 ( echo one; false; echo two; )
143 echo three
144 ## status: 1
145 ## STDOUT:
146 one
147 ## END
148
149 #### set -o errexit while it's being ignored (moot with strict_errexit)
150 set -o errexit
151 # osh aborts early here
152 if { echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; }; then
153 echo 5;
154 fi
155 echo 6
156 false # this is the one that makes shells fail
157 echo 7
158 ## status: 1
159 ## STDOUT:
160 1
161 2
162 3
163 4
164 5
165 6
166 ## END
167
168 #### set +o errexit while it's being ignored (moot with strict_errexit)
169 set -o errexit
170 if { echo 1; false; echo 2; set +o errexit; echo 3; false; echo 4; }; then
171 echo 5;
172 fi
173 echo 6
174 false # does NOT fail, because we restored it.
175 echo 7
176 ## STDOUT:
177 1
178 2
179 3
180 4
181 5
182 6
183 7
184 ## END
185
186 #### set +o errexit with 2 levels of ignored
187 set -o errexit
188 if { echo 1; ! set +o errexit; echo 2; }; then
189 echo 3
190 fi
191 echo 6
192 false
193 echo 7
194
195 ## STDOUT:
196 1
197 2
198 3
199 6
200 7
201 ## END
202
203 #### setting errexit in a subshell works but doesn't affect parent shell
204 ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; )
205 echo 5
206 false
207 echo 6
208 ## STDOUT:
209 1
210 2
211 3
212 5
213 6
214 ## END
215
216 #### set errexit while it's ignored in a subshell (moot with strict_errexit)
217 set -o errexit
218 if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then
219 echo 5;
220 fi
221 echo 6 # This is executed because the subshell just returns false
222 false
223 echo 7
224 ## status: 1
225 ## STDOUT:
226 1
227 2
228 3
229 4
230 5
231 6
232 ## END
233
234 #### shopt -s strict:all || true while errexit is on
235 set -o errexit
236 shopt -s strict:all || true
237 echo one
238 false # fail
239 echo two
240 ## status: 1
241 ## STDOUT:
242 one
243 ## END
244
245 #### errexit double guard
246 # OSH bug fix. ErrExit needs a counter, not a boolean.
247 set -o errexit
248 if { ! false; false; true; } then
249 echo true
250 fi
251 false
252 echo done
253 ## status: 1
254 ## STDOUT:
255 true
256 ## END
257
258 #### background processes respect errexit
259 set -o errexit
260 { echo one; false; echo two; exit 42; } &
261 wait $!
262 ## status: 1
263 ## STDOUT:
264 one
265 ## END
266
267 #### pipeline process respects errexit
268 set -o errexit
269 # It is respected here.
270 { echo one; false; echo two; } | cat
271
272 # Also respected here.
273 { echo three; echo four; } | while read line; do
274 echo "[$line]"
275 false
276 done
277 echo four
278 ## status: 1
279 ## STDOUT:
280 one
281 [three]
282 ## END
283
284 #### simple command / assign - redir failure DOES respect errexit
285
286 $SH -c '
287 set -o errexit
288 true > /
289 echo builtin status=$?
290 '
291 echo status=$?
292
293 $SH -c '
294 set -o errexit
295 /bin/true > /
296 echo extern status=$?
297 '
298 echo status=$?
299
300 $SH -c '
301 set -o errexit
302 assign=foo > /
303 echo assign status=$?
304 '
305 echo status=$?
306
307 ## STDOUT:
308 status=1
309 status=1
310 status=1
311 ## END
312 ## OK dash STDOUT:
313 status=2
314 status=2
315 status=2
316 ## END
317
318 #### simple command that's an alias - redir failure checked
319
320 # bash 5.2 fixed bash 4.4 bug: this is now checked
321
322 $SH -c '
323 shopt -s expand_aliases
324
325 set -o errexit
326 alias zz="{ echo 1; echo 2; }"
327 zz > /
328 echo alias status=$?
329 '
330 echo status=$?
331
332 ## STDOUT:
333 status=1
334 ## END
335
336 ## BUG dash STDOUT:
337 alias status=2
338 status=0
339 ## END
340
341 ## BUG ash STDOUT:
342 alias status=1
343 status=0
344 ## END
345
346 #### bash atoms [[ (( - redir failure checked
347
348 # bash 5.2 fixed bash 4.4 bug: this is now checked
349
350 case $SH in dash) exit ;; esac
351
352 $SH -c '
353 set -o errexit
354 [[ x = x ]] > /
355 echo dbracket status=$?
356 '
357 echo status=$?
358
359 $SH -c '
360 set -o errexit
361 (( 42 )) > /
362 echo dparen status=$?
363 '
364 echo status=$?
365
366 ## STDOUT:
367 status=1
368 status=1
369 ## END
370
371 ## OK ash STDOUT:
372 status=1
373 status=2
374 ## END
375
376 ## N-I dash STDOUT:
377 ## END
378
379
380 #### brace group - redir failure checked
381
382 # bash 5.2 fixed bash 4.4 bug: this is now checked
383
384 # case from
385 # https://lists.gnu.org/archive/html/bug-bash/2020-05/msg00066.html
386
387 set -o errexit
388
389 { cat ; } < not_exist.txt
390
391 echo status=$?
392 echo 'should not get here'
393
394 ## status: 1
395 ## STDOUT:
396 ## END
397
398 ## BUG dash status: 0
399 ## BUG dash STDOUT:
400 status=2
401 should not get here
402 ## END
403
404 ## BUG ash status: 0
405 ## BUG ash STDOUT:
406 status=1
407 should not get here
408 ## END
409
410
411 #### while loop - redirect failure checked
412
413 # bash 5.2 fixed bash 4.4 bug: this is now checked
414
415 # case from
416 # https://lists.gnu.org/archive/html/bug-bash/2020-05/msg00066.html
417
418 set -o errexit
419
420 while read line; do
421 echo $line
422 done < not_exist.txt
423
424 echo status=$?
425 echo 'should not get here'
426
427 ## status: 1
428 ## STDOUT:
429 ## END
430
431 ## BUG dash status: 0
432 ## BUG dash STDOUT:
433 status=2
434 should not get here
435 ## END
436
437 ## BUG ash status: 0
438 ## BUG ash STDOUT:
439 status=1
440 should not get here
441 ## END
442
443
444 #### set -e enabled in function (regression)
445 foo() {
446 set -e
447 false
448 echo "should be executed"
449 }
450 #foo && true
451 #foo || true
452
453 if foo; then
454 true
455 fi
456
457 echo "should be executed"
458 ## STDOUT:
459 should be executed
460 should be executed
461 ## END
462
463 #### set -e in function #2
464 foo() {
465 set -e
466 false
467 echo "should be executed"
468 }
469 ! foo
470
471 echo "should be executed"
472 ## BUG bash stdout-json: ""
473 ## BUG bash status: 1
474 ## STDOUT:
475 should be executed
476 should be executed
477 ## END
478
479
480 #### Command sub exit code is lost
481 echo ft $(false) $(true)
482 echo status=$?
483
484 set -o errexit
485 shopt -s inherit_errexit || true
486
487 # This changes it
488 #shopt -s command_sub_errexit || true
489
490 echo f $(date %x)
491 echo status=$?
492
493 # compare with
494 # x=$(date %x) # FAILS
495 # local x=$(date %x) # does NOT fail
496
497 echo ft $(false) $(true)
498 echo status=$?
499
500 ## STDOUT:
501 ft
502 status=0
503 f
504 status=0
505 ft
506 status=0
507 ## END
508