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
247 case $SH in mksh) exit ;; esac
248
249 set -o errtrace
250 trap 'echo line=$LINENO' ERR
251
252 ( false; echo subshell )
253
254 x=$( false; echo command sub )
255
256 false & wait
257
258 { false; echo async; } & wait
259
260 false
261 echo ok
262
263 ## STDOUT:
264 line=6
265 subshell
266 line=12
267 async
268 line=14
269 ok
270 ## END
271
272 # ash doesn't reject errtrace, but doesn't implement it
273 ## BUG ash STDOUT:
274 subshell
275 async
276 line=14
277 ok
278 ## END
279
280 ## N-I mksh STDOUT:
281 ## END
282
283
284 #### trap ERR not active in shell functions in (bash behavior)
285
286 trap 'echo line=$LINENO' ERR
287
288 f() {
289 false
290 true
291 }
292
293 f
294
295 ## STDOUT:
296 ## END
297
298 ## N-I mksh STDOUT:
299 line=4
300 ## END
301
302 #### set -o errtrace - trap ERR runs in shell functions
303
304 trap 'echo line=$LINENO' ERR
305
306 passing() {
307 false # line 4
308 true
309 }
310
311 failing() {
312 true
313 false
314 }
315
316 passing
317 failing
318
319 set -o errtrace
320
321 echo 'now with errtrace'
322 passing
323 failing
324
325 echo ok
326
327 ## STDOUT:
328 line=14
329 now with errtrace
330 line=4
331 line=10
332 line=20
333 ok
334 ## END
335
336 ## BUG mksh status: 1
337 ## BUG mksh STDOUT:
338 line=4
339 line=10
340 ## END
341
342 #### trap ERR with "atoms": assignment (( [[
343
344 trap 'echo line=$LINENO' ERR
345
346 x=$(false)
347
348 [[ a == b ]]
349
350 (( 0 ))
351 echo ok
352
353 ## STDOUT:
354 line=3
355 line=5
356 line=7
357 ok
358 ## END
359
360 ## BUG mksh STDOUT:
361 line=3
362 line=3
363 line=7
364 ok
365 ## END
366
367
368 #### trap ERR with for, case, { }
369
370 trap 'echo line=$LINENO' ERR
371
372 for y in 1 2; do
373 false
374 done
375
376 case x in
377 x) false ;;
378 *) false ;;
379 esac
380
381 { false; false; false; }
382 echo ok
383
384 ## STDOUT:
385 line=4
386 line=4
387 line=8
388 line=12
389 line=12
390 line=12
391 ok
392 ## END
393
394 #### trap ERR with redirect
395
396 trap 'echo line=$LINENO' ERR
397
398 false
399
400 { false
401 true
402 } > /zz # error
403 echo ok
404
405 ## STDOUT:
406 line=3
407 line=7
408 ok
409 ## END
410
411 # doesn't update line for redirect
412
413 ## BUG bash/mksh STDOUT:
414 line=3
415 line=3
416 ok
417 ## END
418
419 ## BUG ash STDOUT:
420 line=3
421 ok
422 ## END
423
424
425 #### trap ERR with YSH proc
426
427 case $SH in bash|mksh|ash) exit ;; esac
428
429 # seems the same
430
431 shopt -s ysh:upgrade
432
433 proc handler {
434 echo err
435 }
436
437 if test -f /nope { echo file exists }
438
439 trap handler ERR
440
441 if test -f /nope { echo file exists }
442
443 false || true # not run for the first part here
444 false
445
446 ## status: 1
447 ## STDOUT:
448 err
449 ## END
450
451 ## N-I bash/mksh/ash status: 0
452 ## N-I bash/mksh/ash STDOUT:
453 ## END
454
455 #### trap ERR
456 err() {
457 echo "err [$@] $?"
458 }
459 trap 'err x y' ERR
460
461 echo A
462
463 false
464 echo B
465
466 ( exit 42 )
467 echo C
468
469 trap - ERR # disable trap
470
471 false
472 echo D
473
474 trap 'echo after errexit $?' ERR
475
476 set -o errexit
477
478 ( exit 99 )
479 echo E
480
481 ## status: 99
482 ## STDOUT:
483 A
484 err [x y] 1
485 B
486 err [x y] 42
487 C
488 D
489 after errexit 99
490 ## END
491 ## N-I dash STDOUT:
492 A
493 B
494 C
495 D
496 ## END
497
498 #### trap ERR and pipelines - PIPESTATUS difference
499 case $SH in ash) exit ;; esac
500
501 err() {
502 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
503 }
504 trap 'err' ERR
505
506 echo A
507
508 false
509
510 # succeeds
511 echo B | grep B
512
513 # fails
514 echo C | grep zzz
515
516 echo D | grep zzz | cat
517
518 set -o pipefail
519 echo E | grep zzz | cat
520
521 trap - ERR # disable trap
522
523 echo F | grep zz
524 echo ok
525
526 ## STDOUT:
527 A
528 err [] status=1 [1]
529 B
530 err [] status=1 [0 1]
531 err [] status=1 [0 1 0]
532 ok
533 ## END
534
535 # we don't set PIPESTATUS unless we get a pipeline
536
537 ## OK osh STDOUT:
538 A
539 err [] status=1 []
540 B
541 err [] status=1 [0 1]
542 err [] status=1 [0 1 0]
543 ok
544 ## END
545
546 ## N-I ash STDOUT:
547 ## END
548
549 #### error in trap ERR (recursive)
550 case $SH in dash) exit ;; esac
551
552 err() {
553 echo err status $?
554 false
555 ( exit 2 ) # not recursively triggered
556 echo err 2
557 }
558 trap 'err' ERR
559
560 echo A
561 false
562 echo B
563
564 # Try it with errexit
565 set -e
566 false
567 echo C
568
569 ## status: 1
570 ## STDOUT:
571 A
572 err status 1
573 err 2
574 B
575 err status 1
576 ## END
577 ## N-I dash STDOUT:
578 ## END
579