1 ## oils_failures_allowed: 4
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 # anomaly - it gets printed twice?
133 trap 'echo subshell' ERR
134 (false) | (false) | (false) | (false)
135
136 trap 'echo dparen' ERR
137 (( 0 )) | (( 0 )) | (( 0 ))
138
139 trap 'echo dbracket' ERR
140 [[ a = b ]] | [[ a = b ]] | [[ a = b ]]
141
142 echo ok
143
144 ## STDOUT:
145 assign
146 subshell
147 dparen
148 dbracket
149 ok
150 ## END
151
152 ## BUG bash STDOUT:
153 assign
154 subshell
155 subshell
156 dparen
157 dbracket
158 ok
159 ## END
160
161
162 #### trap ERR does not run in errexit situations
163
164 trap 'echo line=$LINENO' ERR
165
166 if false; then
167 echo if
168 fi
169
170 while false; do
171 echo while
172 done
173
174 until false; do
175 echo until
176 break
177 done
178
179 false || false || false
180
181 false && false && false
182
183 false; false; false
184
185 echo ok
186
187 ## STDOUT:
188 until
189 line=16
190 line=20
191 line=20
192 line=20
193 ok
194 ## END
195
196
197 #### trap ERR doesn't run in subprograms - subshell, command sub, async
198
199 trap 'echo line=$LINENO' ERR
200
201 ( false; echo subshell )
202
203 x=$( false; echo command sub )
204
205 false & wait
206
207 { false; echo async; } & wait
208
209 false
210 echo ok
211
212 ## STDOUT:
213 subshell
214 async
215 line=11
216 ok
217 ## END
218
219 #### set -o errtrace: trap ERR runs in subprograms
220
221 case $SH in mksh) exit ;; esac
222
223 set -o errtrace
224 trap 'echo line=$LINENO' ERR
225
226 ( false; echo subshell )
227
228 x=$( false; echo command sub )
229
230 false & wait
231
232 { false; echo async; } & wait
233
234 false
235 echo ok
236
237 ## STDOUT:
238 line=6
239 subshell
240 line=12
241 async
242 line=14
243 ok
244 ## END
245
246 # ash doesn't reject errtrace, but doesn't implement it
247 ## BUG ash STDOUT:
248 subshell
249 async
250 line=14
251 ok
252 ## END
253
254 ## N-I mksh STDOUT:
255 ## END
256
257
258 #### trap ERR not active in shell functions in (bash behavior)
259
260 trap 'echo line=$LINENO' ERR
261
262 f() {
263 false
264 true
265 }
266
267 f
268
269 ## STDOUT:
270 ## END
271
272 ## N-I mksh STDOUT:
273 line=4
274 ## END
275
276 #### set -o errtrace - trap ERR runs in shell functions
277
278 trap 'echo line=$LINENO' ERR
279
280 passing() {
281 false # line 4
282 true
283 }
284
285 failing() {
286 true
287 false
288 }
289
290 passing
291 failing
292
293 set -o errtrace
294
295 echo 'now with errtrace'
296 passing
297 failing
298
299 echo ok
300
301 ## STDOUT:
302 line=14
303 now with errtrace
304 line=4
305 line=10
306 line=20
307 ok
308 ## END
309
310 ## BUG mksh status: 1
311 ## BUG mksh STDOUT:
312 line=4
313 line=10
314 ## END
315
316 #### trap ERR with "atoms": assignment (( [[
317
318 trap 'echo line=$LINENO' ERR
319
320 x=$(false)
321
322 [[ a == b ]]
323
324 (( 0 ))
325 echo ok
326
327 ## STDOUT:
328 line=3
329 line=5
330 line=7
331 ok
332 ## END
333
334 ## BUG mksh STDOUT:
335 line=3
336 line=3
337 line=7
338 ok
339 ## END
340
341
342 #### trap ERR with for, case, { }
343
344 trap 'echo line=$LINENO' ERR
345
346 for y in 1 2; do
347 false
348 done
349
350 case x in
351 x) false ;;
352 *) false ;;
353 esac
354
355 { false; false; }
356 echo ok
357
358 ## STDOUT:
359 line=4
360 line=4
361 line=8
362 line=12
363 line=12
364 ok
365 ## END
366
367 #### trap ERR with redirect
368
369 trap 'echo line=$LINENO' ERR
370
371 false
372
373 { false
374 true
375 } > /zz # error
376 echo ok
377
378 ## STDOUT:
379 line=3
380 line=7
381 ok
382 ## END
383
384 # doesn't update line for redirect
385
386 ## BUG bash/mksh STDOUT:
387 line=3
388 line=3
389 ok
390 ## END
391
392 ## BUG ash STDOUT:
393 line=3
394 ok
395 ## END
396
397
398 #### trap ERR with YSH proc
399
400 case $SH in bash|mksh|ash) exit ;; esac
401
402 # seems the same
403
404 shopt -s ysh:upgrade
405
406 proc handler {
407 echo err
408 }
409
410 if test -f /nope { echo file exists }
411
412 trap handler ERR
413
414 if test -f /nope { echo file exists }
415
416 false || true # not run for the first part here
417 false
418
419 ## status: 1
420 ## STDOUT:
421 err
422 ## END
423
424 ## N-I bash/mksh/ash status: 0
425 ## N-I bash/mksh/ash STDOUT:
426 ## END
427
428 #### trap ERR
429 err() {
430 echo "err [$@] $?"
431 }
432 trap 'err x y' ERR
433
434 echo A
435
436 false
437 echo B
438
439 ( exit 42 )
440 echo C
441
442 trap - ERR # disable trap
443
444 false
445 echo D
446
447 trap 'echo after errexit $?' ERR
448
449 set -o errexit
450
451 ( exit 99 )
452 echo E
453
454 ## status: 99
455 ## STDOUT:
456 A
457 err [x y] 1
458 B
459 err [x y] 42
460 C
461 D
462 after errexit 99
463 ## END
464 ## N-I dash STDOUT:
465 A
466 B
467 C
468 D
469 ## END
470
471 #### trap ERR and pipelines (lastpipe and PIPESTATUS difference)
472 case $SH in ash) exit ;; esac
473
474 err() {
475 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
476 }
477 trap 'err' ERR
478
479 echo A
480
481 false
482
483 # succeeds
484 echo B | grep B
485
486 # fails
487 echo C | grep zzz
488
489 echo D | grep zzz | cat
490
491 set -o pipefail
492 echo E | grep zzz | cat
493
494 trap - ERR # disable trap
495
496 echo F | grep zz
497 echo ok
498
499 ## STDOUT:
500 A
501 err [] status=1 [1]
502 B
503 err [] status=1 [0 1]
504 err [] status=1 [0 1 0]
505 ok
506 ## END
507
508 # lastpipe semantics mean we get another call!
509 # also we don't set PIPESTATUS unless we get a pipeline
510
511 ## OK osh STDOUT:
512 A
513 err [] status=1 []
514 B
515 err [] status=1 [0 0]
516 err [] status=1 [0 1]
517 err [] status=1 [0 1 0]
518 ok
519 ## END
520
521 ## N-I ash STDOUT:
522 ## END
523
524 #### error in trap ERR (recursive)
525 case $SH in dash) exit ;; esac
526
527 err() {
528 echo err status $?
529 false
530 ( exit 2 ) # not recursively triggered
531 echo err 2
532 }
533 trap 'err' ERR
534
535 echo A
536 false
537 echo B
538
539 # Try it with errexit
540 set -e
541 false
542 echo C
543
544 ## status: 1
545 ## STDOUT:
546 A
547 err status 1
548 err 2
549 B
550 err status 1
551 ## END
552 ## N-I dash STDOUT:
553 ## END
554