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