1 ## oils_failures_allowed: 1
2 ## compare_shells: bash
3
4 # Notes on bash semantics:
5
6 # https://www.gnu.org/software/bash/manual/bash.html
7 #
8 # Commands specified with a DEBUG trap are executed before every simple
9 # command, for command, case command, select command, every arithmetic for
10 # command, and before the first command executes in a shell function. The DEBUG
11 # trap is not inherited by shell functions unless the function has been given
12 # the trace attribute or the functrace option has been enabled using the shopt
13 # builtin. The extdebug shell option has additional effects on the DEBUG trap.
14
15 # The trap builtin (see Bourne Shell Builtins) allows an ERR pseudo-signal
16 # specification, similar to EXIT and DEBUG. Commands specified with an ERR trap
17 # are executed after a simple command fails, with a few exceptions. The ERR
18 # trap is not inherited by shell functions unless the -o errtrace option to the
19 # set builtin is enabled.
20
21
22 #### trap -l
23 trap -l | grep INT >/dev/null
24 ## status: 0
25
26
27 #### trap -p
28
29 trap 'echo exit' EXIT
30
31 trap -p > parent.txt
32
33 grep EXIT parent.txt >/dev/null
34 if test $? -eq 0; then
35 echo shown
36 else
37 echo not shown
38 fi
39
40 ## STDOUT:
41 shown
42 exit
43 ## END
44
45 #### trap -p in child is BUGGY in bash
46
47 # It shows the trap even though it doesn't execute it!
48
49 trap 'echo exit' EXIT
50
51 trap -p | cat > child.txt
52
53 grep EXIT child.txt >/dev/null
54 if test $? -eq 0; then
55 echo shown
56 else
57 echo not shown
58 fi
59
60 ## STDOUT:
61 not shown
62 exit
63 ## END
64 ## BUG bash STDOUT:
65 shown
66 exit
67 ## END
68
69 #### trap DEBUG ignores $?
70 debuglog() {
71 echo " [$@]"
72 return 42 # IGNORED FAILURE
73 }
74
75 trap 'debuglog $LINENO' DEBUG
76
77 echo status=$?
78 echo A
79 echo status=$?
80 echo B
81 echo status=$?
82
83 ## STDOUT:
84 [8]
85 status=0
86 [9]
87 A
88 [10]
89 status=0
90 [11]
91 B
92 [12]
93 status=0
94 ## END
95
96 #### but trap DEBUG respects errexit
97 set -o errexit
98
99 debuglog() {
100 echo " [$@]"
101 return 42
102 }
103
104 trap 'debuglog $LINENO' DEBUG
105
106 echo status=$?
107 echo A
108 echo status=$?
109 echo B
110 echo status=$?
111
112 ## status: 42
113 ## STDOUT:
114 [10]
115 ## END
116
117 #### trap DEBUG with 'return'
118
119 debuglog() {
120 echo " [$@]"
121 }
122
123 trap 'debuglog $LINENO; return 42' DEBUG
124
125 echo status=$?
126 echo A
127 echo status=$?
128 echo B
129 echo status=$?
130
131 ## status: 0
132
133 ## STDOUT:
134 [7]
135 status=0
136 [8]
137 A
138 [9]
139 status=0
140 [10]
141 B
142 [11]
143 status=0
144 ## END
145
146 # OSH doesn't ignore this
147
148 ## OK osh status: 42
149 ## OK osh STDOUT:
150 [7]
151 ## END
152
153 #### trap DEBUG with 'exit'
154 debuglog() {
155 echo " [$@]"
156 }
157
158 trap 'debuglog $LINENO; exit 42' DEBUG
159
160 echo status=$?
161 echo A
162 echo status=$?
163 echo B
164 echo status=$?
165
166 ## status: 42
167 ## STDOUT:
168 [7]
169 ## END
170
171
172
173 #### trap DEBUG with non-compound commands
174 case $SH in (dash|mksh) exit ;; esac
175
176 debuglog() {
177 echo " [$@]"
178 }
179 trap 'debuglog $LINENO' DEBUG
180
181 echo a
182 echo b; echo c
183
184 echo d && echo e
185 echo f || echo g
186
187 (( h = 42 ))
188 [[ j == j ]]
189
190 var=value
191
192 readonly r=value
193
194 ## STDOUT:
195 [8]
196 a
197 [9]
198 b
199 [9]
200 c
201 [11]
202 d
203 [11]
204 e
205 [12]
206 f
207 [14]
208 [15]
209 [17]
210 [19]
211 ## END
212
213 #### trap DEBUG and control flow
214
215 debuglog() {
216 echo " [$@]"
217 }
218 trap 'debuglog $LINENO' DEBUG
219
220 while true; do
221 echo hello
222 break
223 done
224
225 ## STDOUT:
226 [6]
227 [7]
228 hello
229 [8]
230 ## END
231 ## STDOUT:
232 [6]
233 [7]
234 hello
235 [8]
236 ## END
237
238 #### trap DEBUG and command sub / subshell
239 case $SH in (dash|mksh) exit ;; esac
240
241 debuglog() {
242 echo " [$@]"
243 }
244 trap 'debuglog $LINENO' DEBUG
245
246 echo "result = $(echo command sub; echo two)"
247 ( echo subshell
248 echo two
249 )
250 echo done
251
252 ## STDOUT:
253 [8]
254 result = command sub
255 two
256 subshell
257 two
258 [12]
259 done
260 ## END
261
262 #### trap DEBUG not run in forked interpreter for first pipeline part
263
264 debuglog() {
265 #echo " PID=$$ BASHPID=$BASHPID LINENO=$1"
266 echo " LINENO=$1"
267 }
268 trap 'debuglog $LINENO' DEBUG
269
270 { echo pipe1;
271 echo pipe2; } \
272 | cat
273 echo ok
274
275 ## STDOUT:
276 LINENO=8
277 pipe1
278 pipe2
279 LINENO=9
280 ok
281 ## END
282
283 #### One 'echo' in first pipeline part - why does bash behave differently from case above?
284
285 # TODO: bash runs the trap 3 times, and osh only twice. I don't see why. Is
286 # it because Process::Run() does trap_state.ClearForSubProgram()? Probably
287 #echo top PID=$$ BASHPID=$BASHPID
288 #shopt -s lastpipe
289
290 debuglog() {
291 #echo " PID=$$ BASHPID=$BASHPID LINENO=$1"
292 #echo " LINENO=$1 $BASH_COMMAND"
293 # LINENO=6 echo pipeline
294 # LINENO=7 cat
295 echo " LINENO=$1"
296 }
297 trap 'debuglog $LINENO' DEBUG
298
299 echo pipeline \
300 | cat
301 echo ok
302
303 ## STDOUT:
304 LINENO=6
305 LINENO=7
306 pipeline
307 LINENO=8
308 ok
309 ## END
310 ## OK osh STDOUT:
311 LINENO=7
312 pipeline
313 LINENO=8
314 ok
315 ## END
316
317 #### trap DEBUG and pipeline (lastpipe difference)
318 debuglog() {
319 echo " [$@]"
320 }
321 trap 'debuglog $LINENO' DEBUG
322
323 # gets run for each one of these
324 { echo a; echo b; }
325
326 # only run for the last one, maybe I guess because traps aren't inherited?
327 { echo x; echo y; } | wc -l
328
329 # bash runs for all of these, but OSH doesn't because we have SubProgramThunk
330 # Hm.
331 date | cat | wc -l
332
333 date |
334 cat |
335 wc -l
336
337 ## STDOUT:
338 [6]
339 a
340 [6]
341 b
342 [8]
343 2
344 [10]
345 [10]
346 [10]
347 1
348 [12]
349 [13]
350 [14]
351 1
352 ## END
353
354 # Marking OK due to lastpipe execution difference
355
356 ## OK osh STDOUT:
357 [6]
358 a
359 [6]
360 b
361 [8]
362 2
363 [10]
364 1
365 [14]
366 1
367 ## END
368
369 #### trap DEBUG function call
370 debuglog() {
371 echo " [$@]"
372 }
373 trap 'debuglog $LINENO' DEBUG
374
375 f() {
376 local mylocal=1
377 for i in "$@"; do
378 echo i=$i
379 done
380 }
381
382 f A B # executes ONCE here, but does NOT go into the function call
383
384 echo next
385
386 f X Y
387
388 echo ok
389
390 ## STDOUT:
391 [13]
392 i=A
393 i=B
394 [15]
395 next
396 [17]
397 i=X
398 i=Y
399 [19]
400 ok
401 ## END
402
403 #### trap DEBUG case
404 debuglog() {
405 echo " [$@]"
406 }
407 trap 'debuglog $LINENO' DEBUG
408
409 name=foo.py
410
411 case $name in
412 *.py)
413 echo python
414 ;;
415 *.sh)
416 echo shell
417 ;;
418 esac
419 echo ok
420
421 ## STDOUT:
422 [6]
423 [8]
424 [10]
425 python
426 [16]
427 ok
428 ## END
429
430 #### trap DEBUG for each
431
432 debuglog() {
433 echo " [$@]"
434 }
435 trap 'debuglog $LINENO' DEBUG
436
437 for x in 1 2; do
438 echo x=$x
439 done
440
441 echo ok
442
443 ## STDOUT:
444 [6]
445 [7]
446 x=1
447 [6]
448 [7]
449 x=2
450 [10]
451 ok
452 ## END
453
454 # NOT matching bash right now because 'while' loops don't have it
455 # And we have MORE LOOPS
456 #
457 # What we really need is a trap that runs in the main loop and TELLS you what
458 # kind of node it is?
459
460 ## N-I osh STDOUT:
461 [7]
462 x=1
463 [7]
464 x=2
465 [10]
466 ok
467 ## END
468
469 #### trap DEBUG for expr
470 debuglog() {
471 echo " [$@]"
472 }
473 trap 'debuglog $LINENO' DEBUG
474
475 for (( i =3 ; i < 5; ++i )); do
476 echo i=$i
477 done
478
479 echo ok
480
481 ## STDOUT:
482 [6]
483 [6]
484 [7]
485 i=3
486 [6]
487 [6]
488 [7]
489 i=4
490 [6]
491 [6]
492 [10]
493 ok
494 ## END
495 ## N-I osh STDOUT:
496 [7]
497 i=3
498 [7]
499 i=4
500 [10]
501 ok
502 ## END
503
504 #### trap DEBUG if while
505 debuglog() {
506 echo " [$@]"
507 }
508 trap 'debuglog $LINENO' DEBUG
509
510 if test x = x; then
511 echo if
512 fi
513
514 while test x != x; do
515 echo while
516 done
517
518 ## STDOUT:
519 [6]
520 [7]
521 if
522 [10]
523 ## END
524
525
526 #### trap RETURN
527 profile() {
528 echo "profile [$@]"
529 }
530 g() {
531 echo --
532 echo g
533 echo --
534 return
535 }
536 f() {
537 echo --
538 echo f
539 echo --
540 g
541 }
542 # RETURN trap doesn't fire when a function returns, only when a script returns?
543 # That's not what the manual says.
544 trap 'profile x y' RETURN
545 f
546 . $REPO_ROOT/spec/testdata/return-helper.sh
547 ## status: 42
548 ## STDOUT:
549 --
550 f
551 --
552 --
553 g
554 --
555 return-helper.sh
556 profile [x y]
557 ## END