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