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 #### trap ERR does not run in errexit situations
85
86 trap 'echo line=$LINENO' ERR
87
88 if false; then
89 echo if
90 fi
91
92 while false; do
93 echo while
94 done
95
96 until false; do
97 echo until
98 break
99 done
100
101 false || false || false
102
103 false && false && false
104
105 false; false; false
106
107 echo ok
108
109 ## STDOUT:
110 until
111 line=16
112 line=20
113 line=20
114 line=20
115 ok
116 ## END
117
118 #### trap ERR pipeline (also errexit)
119
120 # mksh and bash have different line numbers in this case
121 #trap 'echo line=$LINENO' ERR
122 trap 'echo line=$LINENO' ERR
123
124 # it's run for the last 'false'
125 false | false | false
126
127 { echo pipeline; false; } | false | false
128
129 # it's never run here
130 ! true
131 ! false
132
133 ## STDOUT:
134 line=3
135 line=5
136 ## END
137
138 ## BUG mksh/ash STDOUT:
139 line=1
140 line=1
141 ## END
142
143 #### trap ERR subprogram - subshell, command sub, async
144
145 trap 'echo line=$LINENO' ERR
146
147 ( false; echo subshell )
148
149 x=$( false; echo command sub )
150
151 false & wait
152
153 { false; echo async; } & wait
154
155 false
156 echo ok
157
158 ## STDOUT:
159 subshell
160 async
161 line=11
162 ok
163 ## END
164
165 #### trap ERR not active in shell functions in (bash behavior)
166
167 trap 'echo line=$LINENO' ERR
168
169 f() {
170 false
171 true
172 }
173
174 f
175
176 ## STDOUT:
177 ## END
178
179 ## N-I mksh STDOUT:
180 line=4
181 ## END
182
183 #### trap ERR shell function - with errtrace
184
185 trap 'echo line=$LINENO' ERR
186
187 passing() {
188 false # line 4
189 true
190 }
191
192 failing() {
193 true
194 false
195 }
196
197 passing
198 failing
199
200 set -o errtrace
201
202 echo 'now with errtrace'
203 passing
204 failing
205
206 echo ok
207
208 ## STDOUT:
209 line=14
210 now with errtrace
211 line=4
212 line=10
213 line=20
214 ok
215 ## END
216
217 ## BUG mksh status: 1
218 ## BUG mksh STDOUT:
219 line=4
220 line=10
221 ## END
222
223
224 #### trap ERR with YSH proc
225
226 case $SH in bash|mksh|ash) exit ;; esac
227
228 # seems the same
229
230 shopt -s ysh:upgrade
231
232 proc abc { echo abc }
233 if test -f /nope { echo file exists }
234 trap abc ERR
235 if test -f /nope { echo file exists }
236
237 ## STDOUT:
238 abc
239 ## END
240
241 ## N-I bash/mksh/ash STDOUT:
242 ## END
243
244 #### trap ERR
245 err() {
246 echo "err [$@] $?"
247 }
248 trap 'err x y' ERR
249
250 echo A
251
252 false
253 echo B
254
255 ( exit 42 )
256 echo C
257
258 trap - ERR # disable trap
259
260 false
261 echo D
262
263 trap 'echo after errexit $?' ERR
264
265 set -o errexit
266
267 ( exit 99 )
268 echo E
269
270 ## status: 99
271 ## STDOUT:
272 A
273 err [x y] 1
274 B
275 err [x y] 42
276 C
277 D
278 after errexit 99
279 ## END
280 ## N-I dash STDOUT:
281 A
282 B
283 C
284 D
285 ## END
286
287 #### trap ERR and pipelines (lastpipe and PIPESTATUS difference)
288 case $SH in ash) exit ;; esac
289
290 err() {
291 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
292 }
293 trap 'err' ERR
294
295 echo A
296
297 false
298
299 # succeeds
300 echo B | grep B
301
302 # fails
303 echo C | grep zzz
304
305 echo D | grep zzz | cat
306
307 set -o pipefail
308 echo E | grep zzz | cat
309
310 trap - ERR # disable trap
311
312 echo F | grep zz
313 echo ok
314
315 ## STDOUT:
316 A
317 err [] status=1 [1]
318 B
319 err [] status=1 [0 1]
320 err [] status=1 [0 1 0]
321 ok
322 ## END
323
324 # lastpipe semantics mean we get another call!
325 # also we don't set PIPESTATUS unless we get a pipeline
326
327 ## OK osh STDOUT:
328 A
329 err [] status=1 []
330 B
331 err [] status=1 [0 0]
332 err [] status=1 [0 1]
333 err [] status=1 [0 1 0]
334 ok
335 ## END
336
337 ## N-I ash STDOUT:
338 ## END
339
340 #### error in trap ERR (recursive)
341 case $SH in dash) exit ;; esac
342
343 err() {
344 echo err status $?
345 false
346 ( exit 2 ) # not recursively triggered
347 echo err 2
348 }
349 trap 'err' ERR
350
351 echo A
352 false
353 echo B
354
355 # Try it with errexit
356 set -e
357 false
358 echo C
359
360 ## status: 1
361 ## STDOUT:
362 A
363 err status 1
364 err 2
365 B
366 err status 1
367 ## END
368 ## N-I dash STDOUT:
369 ## END
370