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