1 ## oils_failures_allowed: 2
2 ## compare_shells: bash mksh ash
3
4 # Notes on bash semantics:
5 #
6 # https://www.gnu.org/software/bash/manual/bash.html
7 #
8 # The trap builtin (see Bourne Shell Builtins) allows an ERR pseudo-signal
9 # specification, similar to EXIT and DEBUG. Commands specified with an ERR trap
10 # are executed after a simple command fails, with a few exceptions. The ERR
11 # trap is not inherited by shell functions unless the -o errtrace option to the
12 # set builtin is enabled.
13
14
15 #### trap can use original $LINENO
16
17 trap 'echo line=$LINENO' ERR
18
19 false
20 false
21 echo ok
22
23 ## STDOUT:
24 line=3
25 line=4
26 ok
27 ## END
28
29 #### trap ERR and set -o errexit
30
31 trap 'echo line=$LINENO' ERR
32
33 false
34 echo a
35
36 set -o errexit
37
38 echo b
39 false # trap executed, and executation also halts
40 echo c # doesn't get here
41
42 ## status: 1
43 ## STDOUT:
44 line=3
45 a
46 b
47 line=9
48 ## END
49
50 #### trap ERR and errexit disabled context
51
52 trap 'echo line=$LINENO' ERR
53
54 false
55 echo a
56
57 set -o errexit
58
59 echo b
60 if false; then
61 echo xx
62 fi
63 echo c # doesn't get here
64
65 ## STDOUT:
66 line=3
67 a
68 b
69 c
70 ## END
71
72 #### trap ERR and if statement
73
74 if test -f /nope; then echo file exists; fi
75
76 trap 'echo err' ERR
77 #trap 'echo line=$LINENO' ERR
78
79 if test -f /nope; then echo file exists; fi
80
81 ## STDOUT:
82 ## END
83
84
85 #### trap ERR and || conditional
86
87 trap 'echo line=$LINENO' ERR
88
89 false || false || false
90 echo ok
91
92 false && false
93 echo ok
94
95 ## STDOUT:
96 line=3
97 ok
98 ok
99 ## END
100
101 #### trap ERR and pipeline
102
103 # mksh and bash have different line numbers in this case
104 #trap 'echo line=$LINENO' ERR
105 trap 'echo line=$LINENO' ERR
106
107 # it's run for the last 'false'
108 false | false | false
109
110 { echo pipeline; false; } | false | false
111
112 # it's never run here
113 ! true
114 ! false
115
116 ## STDOUT:
117 line=3
118 line=5
119 ## END
120
121 ## BUG mksh/ash STDOUT:
122 line=1
123 line=1
124 ## END
125
126
127 #### trap ERR pipelines without simple commands
128
129 trap 'echo assign' ERR
130 a=$(false) | a=$(false) | a=$(false)
131
132 trap 'echo dparen' ERR
133 (( 0 )) | (( 0 )) | (( 0 ))
134
135 trap 'echo dbracket' ERR
136 [[ a = b ]] | [[ a = b ]] | [[ a = b ]]
137
138 # bash anomaly - it gets printed twice?
139 trap 'echo subshell' ERR
140 (false) | (false) | (false) | (false)
141
142 # same bug
143 trap 'echo subshell2' ERR
144 (false) | (false) | (false) | (false; false)
145
146 trap 'echo group' ERR
147 { false; } | { false; } | { false; }
148
149 echo ok
150
151 ## STDOUT:
152 assign
153 dparen
154 dbracket
155 subshell
156 subshell2
157 group
158 ok
159 ## END
160
161 ## BUG bash STDOUT:
162 assign
163 dparen
164 dbracket
165 subshell
166 subshell
167 subshell2
168 subshell2
169 group
170 ok
171 ## END
172
173
174 #### Pipeline group quirk
175
176 # Oh this is because it's run for the PIPELINE, not for the last thing! Hmmm
177
178 trap 'echo group2' ERR
179 { false; } | { false; } | { false; false; }
180
181 echo ok
182
183 ## STDOUT:
184 group2
185 ok
186 ## END
187
188 #### trap ERR does not run in errexit situations
189
190 trap 'echo line=$LINENO' ERR
191
192 if false; then
193 echo if
194 fi
195
196 while false; do
197 echo while
198 done
199
200 until false; do
201 echo until
202 break
203 done
204
205 false || false || false
206
207 false && false && false
208
209 false; false; false
210
211 echo ok
212
213 ## STDOUT:
214 until
215 line=16
216 line=20
217 line=20
218 line=20
219 ok
220 ## END
221
222
223 #### trap ERR doesn't run in subprograms - subshell, command sub, async
224
225 trap 'echo line=$LINENO' ERR
226
227 ( false; echo subshell )
228
229 x=$( false; echo command sub )
230
231 false & wait
232
233 { false; echo async; } & wait
234
235 false
236 echo ok
237
238 ## STDOUT:
239 subshell
240 async
241 line=11
242 ok
243 ## END
244
245 #### set -o errtrace: trap ERR runs in subprograms
246 case $SH in mksh) exit ;; esac
247
248 set -o errtrace
249 trap 'echo line=$LINENO' ERR
250
251 ( false; echo subshell )
252
253 x=$( false; echo command sub )
254
255 false
256 echo ok
257
258 ## STDOUT:
259 line=6
260 subshell
261 line=10
262 ok
263 ## END
264
265 # ash doesn't reject errtrace, but doesn't implement it
266 ## BUG ash STDOUT:
267 subshell
268 line=10
269 ok
270 ## END
271
272 ## N-I mksh STDOUT:
273 ## END
274
275 #### trap ERR doesn't run with &
276
277 trap 'echo line=$LINENO' ERR
278
279 false & wait
280
281 { false; echo async; } & wait
282
283 ## STDOUT:
284 async
285 ## END
286
287
288 #### set -o errtrace: trap ERR with &
289 case $SH in mksh) exit ;; esac
290
291 set -o errtrace
292 trap 'echo line=$LINENO' ERR
293
294 false & wait
295
296 { false; echo async; } & wait
297
298 ## STDOUT:
299 line=8
300 async
301 ## END
302
303 ## BUG ash STDOUT:
304 async
305 ## END
306
307 ## N-I mksh STDOUT:
308 ## END
309
310
311
312 #### trap ERR not active in shell functions in (bash behavior)
313
314 trap 'echo line=$LINENO' ERR
315
316 f() {
317 false
318 true
319 }
320
321 f
322
323 ## STDOUT:
324 ## END
325
326 ## N-I mksh STDOUT:
327 line=4
328 ## END
329
330 #### set -o errtrace - trap ERR runs in shell functions
331
332 trap 'echo err' ERR
333
334 passing() {
335 false # line 4
336 true
337 }
338
339 failing() {
340 true
341 false
342 }
343
344 passing
345 failing
346
347 set -o errtrace
348
349 echo 'now with errtrace'
350 passing
351 failing
352
353 echo ok
354
355 ## STDOUT:
356 err
357 now with errtrace
358 err
359 err
360 err
361 ok
362 ## END
363
364 ## BUG mksh status: 1
365 ## BUG mksh STDOUT:
366 err
367 err
368 ## END
369
370 #### set -o errtrace - trap ERR runs in shell functions (LINENO)
371
372 trap 'echo line=$LINENO' ERR
373
374 passing() {
375 false # line 4
376 true
377 }
378
379 failing() {
380 true
381 false
382 }
383
384 passing
385 failing
386
387 set -o errtrace
388
389 echo 'now with errtrace'
390 passing
391 failing
392
393 echo ok
394
395 ## STDOUT:
396 line=14
397 now with errtrace
398 line=4
399 line=10
400 line=20
401 ok
402 ## END
403
404 ## BUG mksh status: 1
405 ## BUG mksh STDOUT:
406 line=4
407 line=10
408 ## END
409
410 #### trap ERR with "atoms": assignment (( [[
411
412 trap 'echo line=$LINENO' ERR
413
414 x=$(false)
415
416 [[ a == b ]]
417
418 (( 0 ))
419 echo ok
420
421 ## STDOUT:
422 line=3
423 line=5
424 line=7
425 ok
426 ## END
427
428 ## BUG mksh STDOUT:
429 line=3
430 line=3
431 line=7
432 ok
433 ## END
434
435
436 #### trap ERR with for, case, { }
437
438 trap 'echo line=$LINENO' ERR
439
440 for y in 1 2; do
441 false
442 done
443
444 case x in
445 x) false ;;
446 *) false ;;
447 esac
448
449 { false; false; false; }
450 echo ok
451
452 ## STDOUT:
453 line=4
454 line=4
455 line=8
456 line=12
457 line=12
458 line=12
459 ok
460 ## END
461
462 #### trap ERR with redirect
463
464 trap 'echo line=$LINENO' ERR
465
466 false
467
468 { false
469 true
470 } > /zz # error
471 echo ok
472
473 ## STDOUT:
474 line=3
475 line=7
476 ok
477 ## END
478
479 # doesn't update line for redirect
480
481 ## BUG bash/mksh STDOUT:
482 line=3
483 line=3
484 ok
485 ## END
486
487 ## BUG ash STDOUT:
488 line=3
489 ok
490 ## END
491
492
493 #### trap ERR with YSH proc
494
495 case $SH in bash|mksh|ash) exit ;; esac
496
497 # seems the same
498
499 shopt -s ysh:upgrade
500
501 proc handler {
502 echo err
503 }
504
505 if test -f /nope { echo file exists }
506
507 trap handler ERR
508
509 if test -f /nope { echo file exists }
510
511 false || true # not run for the first part here
512 false
513
514 ## status: 1
515 ## STDOUT:
516 err
517 ## END
518
519 ## N-I bash/mksh/ash status: 0
520 ## N-I bash/mksh/ash STDOUT:
521 ## END
522
523 #### trap ERR
524 err() {
525 echo "err [$@] $?"
526 }
527 trap 'err x y' ERR
528
529 echo A
530
531 false
532 echo B
533
534 ( exit 42 )
535 echo C
536
537 trap - ERR # disable trap
538
539 false
540 echo D
541
542 trap 'echo after errexit $?' ERR
543
544 set -o errexit
545
546 ( exit 99 )
547 echo E
548
549 ## status: 99
550 ## STDOUT:
551 A
552 err [x y] 1
553 B
554 err [x y] 42
555 C
556 D
557 after errexit 99
558 ## END
559 ## N-I dash STDOUT:
560 A
561 B
562 C
563 D
564 ## END
565
566 #### trap ERR and pipelines - PIPESTATUS difference
567 case $SH in ash) exit ;; esac
568
569 err() {
570 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
571 }
572 trap 'err' ERR
573
574 echo A
575
576 false
577
578 # succeeds
579 echo B | grep B
580
581 # fails
582 echo C | grep zzz
583
584 echo D | grep zzz | cat
585
586 set -o pipefail
587 echo E | grep zzz | cat
588
589 trap - ERR # disable trap
590
591 echo F | grep zz
592 echo ok
593
594 ## STDOUT:
595 A
596 err [] status=1 [1]
597 B
598 err [] status=1 [0 1]
599 err [] status=1 [0 1 0]
600 ok
601 ## END
602
603 # we don't set PIPESTATUS unless we get a pipeline
604
605 ## OK osh STDOUT:
606 A
607 err [] status=1 []
608 B
609 err [] status=1 [0 1]
610 err [] status=1 [0 1 0]
611 ok
612 ## END
613
614 ## N-I ash STDOUT:
615 ## END
616
617 #### error in trap ERR (recursive)
618 case $SH in dash) exit ;; esac
619
620 err() {
621 echo err status $?
622 false
623 ( exit 2 ) # not recursively triggered
624 echo err 2
625 }
626 trap 'err' ERR
627
628 echo A
629 false
630 echo B
631
632 # Try it with errexit
633 set -e
634 false
635 echo C
636
637 ## status: 1
638 ## STDOUT:
639 A
640 err status 1
641 err 2
642 B
643 err status 1
644 ## END
645 ## N-I dash STDOUT:
646 ## END
647