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 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 assignment, for, case, { }
243
244 trap 'echo line=$LINENO' ERR
245
246 x=$(false)
247
248 for y in 1 2; do
249 false
250 done
251
252 case x in
253 x) false ;;
254 *) false ;;
255 esac
256
257 { false; false; }
258 echo ok
259
260 ## STDOUT:
261 line=3
262 line=6
263 line=6
264 line=10
265 line=14
266 line=14
267 ok
268 ## END
269
270 #### trap ERR with redirect
271
272 trap 'echo line=$LINENO' ERR
273
274 false
275
276 { false
277 true
278 } > /zz # error
279 echo ok
280
281 ## STDOUT:
282 line=3
283 line=7
284 ok
285 ## END
286
287 # doesn't update line for redirect
288
289 ## BUG bash/mksh STDOUT:
290 line=3
291 line=3
292 ok
293 ## END
294
295 ## BUG ash STDOUT:
296 line=3
297 ok
298 ## END
299
300
301 #### trap ERR with YSH proc
302
303 case $SH in bash|mksh|ash) exit ;; esac
304
305 # seems the same
306
307 shopt -s ysh:upgrade
308
309 proc handler {
310 echo err
311 }
312
313 if test -f /nope { echo file exists }
314
315 trap handler ERR
316
317 if test -f /nope { echo file exists }
318
319 false || true # not run for the first part here
320 false
321
322 ## status: 1
323 ## STDOUT:
324 err
325 ## END
326
327 ## N-I bash/mksh/ash status: 0
328 ## N-I bash/mksh/ash STDOUT:
329 ## END
330
331 #### trap ERR
332 err() {
333 echo "err [$@] $?"
334 }
335 trap 'err x y' ERR
336
337 echo A
338
339 false
340 echo B
341
342 ( exit 42 )
343 echo C
344
345 trap - ERR # disable trap
346
347 false
348 echo D
349
350 trap 'echo after errexit $?' ERR
351
352 set -o errexit
353
354 ( exit 99 )
355 echo E
356
357 ## status: 99
358 ## STDOUT:
359 A
360 err [x y] 1
361 B
362 err [x y] 42
363 C
364 D
365 after errexit 99
366 ## END
367 ## N-I dash STDOUT:
368 A
369 B
370 C
371 D
372 ## END
373
374 #### trap ERR and pipelines (lastpipe and PIPESTATUS difference)
375 case $SH in ash) exit ;; esac
376
377 err() {
378 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
379 }
380 trap 'err' ERR
381
382 echo A
383
384 false
385
386 # succeeds
387 echo B | grep B
388
389 # fails
390 echo C | grep zzz
391
392 echo D | grep zzz | cat
393
394 set -o pipefail
395 echo E | grep zzz | cat
396
397 trap - ERR # disable trap
398
399 echo F | grep zz
400 echo ok
401
402 ## STDOUT:
403 A
404 err [] status=1 [1]
405 B
406 err [] status=1 [0 1]
407 err [] status=1 [0 1 0]
408 ok
409 ## END
410
411 # lastpipe semantics mean we get another call!
412 # also we don't set PIPESTATUS unless we get a pipeline
413
414 ## OK osh STDOUT:
415 A
416 err [] status=1 []
417 B
418 err [] status=1 [0 0]
419 err [] status=1 [0 1]
420 err [] status=1 [0 1 0]
421 ok
422 ## END
423
424 ## N-I ash STDOUT:
425 ## END
426
427 #### error in trap ERR (recursive)
428 case $SH in dash) exit ;; esac
429
430 err() {
431 echo err status $?
432 false
433 ( exit 2 ) # not recursively triggered
434 echo err 2
435 }
436 trap 'err' ERR
437
438 echo A
439 false
440 echo B
441
442 # Try it with errexit
443 set -e
444 false
445 echo C
446
447 ## status: 1
448 ## STDOUT:
449 A
450 err status 1
451 err 2
452 B
453 err status 1
454 ## END
455 ## N-I dash STDOUT:
456 ## END
457