1 # builtin-trap.test.sh
2
3 #### trap -l
4 trap -l | grep INT >/dev/null
5 ## status: 0
6 ## N-I dash/mksh status: 1
7
8 #### trap accepts/ignores --
9 trap -- 'echo hi' EXIT
10 echo done
11 ## STDOUT:
12 done
13 hi
14 ## END
15
16 #### trap 'echo hi' KILL (regression test, caught by smoosh suite)
17 trap 'echo hi' 9
18 echo status=$?
19 trap 'echo hi' KILL
20 echo status=$?
21 trap 'echo hi' STOP
22 echo status=$?
23 trap 'echo hi' TERM
24 echo status=$?
25 ## STDOUT:
26 status=0
27 status=0
28 status=0
29 status=0
30 ## END
31 ## OK osh STDOUT:
32 status=1
33 status=1
34 status=1
35 status=0
36 ## END
37
38 #### trap -p
39 trap 'echo exit' EXIT
40 trap -p | grep EXIT >/dev/null
41 ## status: 0
42 ## N-I dash/mksh status: 1
43
44 #### Register invalid trap
45 trap 'foo' SIGINVALID
46 ## status: 1
47
48 #### Remove invalid trap
49 trap - SIGINVALID
50 ## status: 1
51
52 #### SIGINT and INT are aliases
53 trap - SIGINT
54 echo $?
55 trap - INT
56 echo $?
57 ## STDOUT:
58 0
59 0
60 ## END
61 ## N-I dash STDOUT:
62 1
63 0
64 ## END
65
66 #### Invalid trap invocation
67 trap 'foo'
68 echo status=$?
69 ## stdout: status=2
70 ## OK dash stdout: status=1
71 ## BUG mksh stdout: status=0
72
73 #### exit 1 when trap code string is invalid
74 # All shells spew warnings to stderr, but don't actually exit! Bad!
75 trap 'echo <' EXIT
76 echo status=$?
77 ## stdout: status=1
78 ## BUG mksh status: 1
79 ## BUG mksh stdout: status=0
80 ## BUG dash/bash status: 0
81 ## BUG dash/bash stdout: status=0
82
83 #### trap EXIT calling exit
84 cleanup() {
85 echo "cleanup [$@]"
86 exit 42
87 }
88 trap 'cleanup x y z' EXIT
89 ## stdout: cleanup [x y z]
90 ## status: 42
91
92 #### trap EXIT return status ignored
93 cleanup() {
94 echo "cleanup [$@]"
95 return 42
96 }
97 trap 'cleanup x y z' EXIT
98 ## stdout: cleanup [x y z]
99 ## status: 0
100
101 #### trap EXIT with PARSE error
102 trap 'echo FAILED' EXIT
103 for
104 ## stdout: FAILED
105 ## status: 2
106 ## OK mksh status: 1
107
108 #### trap EXIT with PARSE error and explicit exit
109 trap 'echo FAILED; exit 0' EXIT
110 for
111 ## stdout: FAILED
112 ## status: 0
113
114 #### trap EXIT with explicit exit
115 trap 'echo IN TRAP; echo $stdout' EXIT
116 stdout=FOO
117 exit 42
118
119 ## status: 42
120 ## STDOUT:
121 IN TRAP
122 FOO
123 ## END
124
125 #### trap DEBUG
126 debuglog() {
127 echo "debuglog [$@]"
128 }
129 trap 'debuglog x y' DEBUG
130 echo 1
131 echo 2
132 ## STDOUT:
133 debuglog [x y]
134 1
135 debuglog [x y]
136 2
137 ## END
138 ## N-I dash/mksh STDOUT:
139 1
140 2
141 ## END
142
143 #### trap DEBUG and pipeline
144 case $SH in (dash|mksh) exit 1 ;; esac
145
146 debuglog() {
147 echo " [$@]"
148 }
149 trap 'debuglog $LINENO' DEBUG
150
151 # gets run for each one of these
152 { echo a; echo b; }
153
154 # only run for the last one
155 { echo x; echo y; } | wc -l
156
157 # gets run for both of these
158 date | wc -l
159
160 date |
161 wc -l
162
163 ## STDOUT:
164 [6]
165 a
166 [6]
167 b
168 [7]
169 2
170 [8]
171 [8]
172 1
173 [9]
174 [10]
175 1
176 ## END
177 ## N-I dash/mksh status: 1
178 ## N-I dash/mksh stdout-json: ""
179
180
181 #### trap DEBUG with compound commands
182 case $SH in (dash|mksh) exit 1 ;; esac
183
184 # I'm not sure if the observed behavior actually matches the bash documentation
185 # ...
186 #
187 # https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins
188 #
189 # "If a sigspec is DEBUG, the command arg is executed before every simple
190 # command, for command, case command, select command, every arithmetic for
191 # command, and before the first command executes in a shell function."
192
193 debuglog() {
194 echo " [$@]"
195 }
196 trap 'debuglog $LINENO' DEBUG
197
198 f() {
199 local mylocal=1
200 for i in "$@"; do
201 export i=$i
202 done
203 }
204
205 echo '-- assign --'
206 g=1 # executes ONCE here
207
208 echo '-- function call --'
209 f A B C # executes ONCE here, but does NOT go into th efunction call
210
211
212 echo '-- for --'
213 # why does it execute twice here? because of the for loop? That's not a
214 # simple command.
215 for i in 1 2; do
216 echo for1 $i
217 echo for2 $i
218 done
219
220 echo '-- while --'
221 i=0
222 while (( i < 2 )); do
223 echo while1
224 echo while2
225 (( i++ ))
226 done
227
228 echo '-- if --'
229 if true; then
230 echo IF
231 fi
232
233 echo '-- case --'
234 case x in
235 (x)
236 echo CASE
237 esac
238
239 ## STDOUT:
240 [12]
241 -- assign --
242 [13]
243 [14]
244 -- function call --
245 [15]
246 [16]
247 -- for --
248 [17]
249 [18]
250 for1 1
251 [19]
252 for2 1
253 [17]
254 [18]
255 for1 2
256 [19]
257 for2 2
258 [21]
259 -- while --
260 [22]
261 [23]
262 [24]
263 while1
264 [25]
265 while2
266 [26]
267 [23]
268 [24]
269 while1
270 [25]
271 while2
272 [26]
273 [23]
274 [28]
275 -- if --
276 [29]
277 [30]
278 IF
279 [32]
280 -- case --
281 [33]
282 [35]
283 CASE
284 ## END
285 ## N-I dash/mksh status: 1
286 ## N-I dash/mksh stdout-json: ""
287
288
289 #### trap RETURN
290 profile() {
291 echo "profile [$@]"
292 }
293 g() {
294 echo --
295 echo g
296 echo --
297 return
298 }
299 f() {
300 echo --
301 echo f
302 echo --
303 g
304 }
305 # RETURN trap doesn't fire when a function returns, only when a script returns?
306 # That's not what the manual syas.
307 trap 'profile x y' RETURN
308 f
309 . $REPO_ROOT/spec/testdata/return-helper.sh
310 ## status: 42
311 ## STDOUT:
312 --
313 f
314 --
315 --
316 g
317 --
318 return-helper.sh
319 profile [x y]
320 ## END
321 ## N-I dash/mksh STDOUT:
322 --
323 f
324 --
325 --
326 g
327 --
328 return-helper.sh
329 ## END
330
331 #### trap ERR and disable it
332 err() {
333 echo "err [$@] $?"
334 }
335 trap 'err x y' ERR
336 echo 1
337 false
338 echo 2
339 trap - ERR # disable trap
340 false
341 echo 3
342 ## STDOUT:
343 1
344 err [x y] 1
345 2
346 3
347 ## END
348 ## N-I dash STDOUT:
349 1
350 2
351 3
352 ## END
353
354 #### trap 0 is equivalent to EXIT
355 # not sure why this is, but POSIX wants it.
356 trap 'echo EXIT' 0
357 echo status=$?
358 trap - EXIT
359 echo status=$?
360 ## status: 0
361 ## STDOUT:
362 status=0
363 status=0
364 ## END
365
366 #### trap 1 is equivalent to SIGHUP; HUP is equivalent to SIGHUP
367 trap 'echo HUP' SIGHUP
368 echo status=$?
369 trap 'echo HUP' HUP
370 echo status=$?
371 trap 'echo HUP' 1
372 echo status=$?
373 trap - HUP
374 echo status=$?
375 ## status: 0
376 ## STDOUT:
377 status=0
378 status=0
379 status=0
380 status=0
381 ## END
382 ## N-I dash STDOUT:
383 status=1
384 status=0
385 status=0
386 status=0
387 ## END
388
389 #### eval in the exit trap (regression for issue #293)
390 trap 'eval "echo hi"' 0
391 ## STDOUT:
392 hi
393 ## END
394
395
396 #### exit codes for traps are isolated
397 trap 'echo USR1; ( exit 42)' USR1
398 echo status=$?
399 kill -USR1 $$
400 echo status=$?
401
402 ## STDOUT:
403 status=0
404 USR1
405 status=0
406 ## END