OILS / spec / builtin-trap-bash.test.sh View on Github | oilshell.org

829 lines, 532 significant
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
16trap -l | grep INT >/dev/null
17## status: 0
18
19
20#### trap -p
21
22trap 'echo exit' EXIT
23
24trap -p > parent.txt
25
26grep EXIT parent.txt >/dev/null
27if test $? -eq 0; then
28 echo shown
29else
30 echo not shown
31fi
32
33## STDOUT:
34shown
35exit
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
42trap 'echo exit' EXIT
43
44trap -p | cat > child.txt
45
46grep EXIT child.txt >/dev/null
47if test $? -eq 0; then
48 echo shown
49else
50 echo not shown
51fi
52
53## STDOUT:
54not shown
55exit
56## END
57## BUG bash STDOUT:
58shown
59exit
60## END
61
62#### trap DEBUG ignores $?
63debuglog() {
64 echo " [$@]"
65 return 42 # IGNORED FAILURE
66}
67
68trap 'debuglog $LINENO' DEBUG
69
70echo status=$?
71echo A
72echo status=$?
73echo B
74echo status=$?
75
76## STDOUT:
77 [8]
78status=0
79 [9]
80A
81 [10]
82status=0
83 [11]
84B
85 [12]
86status=0
87## END
88
89#### but trap DEBUG respects errexit
90set -o errexit
91
92debuglog() {
93 echo " [$@]"
94 return 42
95}
96
97trap 'debuglog $LINENO' DEBUG
98
99echo status=$?
100echo A
101echo status=$?
102echo B
103echo status=$?
104
105## status: 42
106## STDOUT:
107 [10]
108## END
109
110#### trap DEBUG with 'return'
111
112debuglog() {
113 echo " [$@]"
114}
115
116trap 'debuglog $LINENO; return 42' DEBUG
117
118echo status=$?
119echo A
120echo status=$?
121echo B
122echo status=$?
123
124## status: 0
125
126## STDOUT:
127 [7]
128status=0
129 [8]
130A
131 [9]
132status=0
133 [10]
134B
135 [11]
136status=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'
147debuglog() {
148 echo " [$@]"
149}
150
151trap 'debuglog $LINENO; exit 42' DEBUG
152
153echo status=$?
154echo A
155echo status=$?
156echo B
157echo status=$?
158
159## status: 42
160## STDOUT:
161 [7]
162## END
163
164
165
166#### trap DEBUG with non-compound commands
167case $SH in (dash|mksh) exit ;; esac
168
169debuglog() {
170 echo " [$@]"
171}
172trap 'debuglog $LINENO' DEBUG
173
174echo a
175echo b; echo c
176
177echo d && echo e
178echo f || echo g
179
180(( h = 42 ))
181[[ j == j ]]
182
183var=value
184
185readonly r=value
186
187## STDOUT:
188 [8]
189a
190 [9]
191b
192 [9]
193c
194 [11]
195d
196 [11]
197e
198 [12]
199f
200 [14]
201 [15]
202 [17]
203 [19]
204## END
205
206#### trap DEBUG and control flow
207
208debuglog() {
209 echo " [$@]"
210}
211trap 'debuglog $LINENO' DEBUG
212
213while true; do
214 echo hello
215 break
216done
217
218## STDOUT:
219 [6]
220 [7]
221hello
222 [8]
223## END
224## STDOUT:
225 [6]
226 [7]
227hello
228 [8]
229## END
230
231#### trap DEBUG and command sub / subshell
232case $SH in (dash|mksh) exit ;; esac
233
234debuglog() {
235 echo " [$@]"
236}
237trap 'debuglog $LINENO' DEBUG
238
239echo "result = $(echo command sub; echo two)"
240( echo subshell
241 echo two
242)
243echo done
244
245## STDOUT:
246 [8]
247result = command sub
248two
249subshell
250two
251 [12]
252done
253## END
254
255#### trap DEBUG not run in forked interpreter for first pipeline part
256
257debuglog() {
258 #echo " PID=$$ BASHPID=$BASHPID LINENO=$1"
259 echo " LINENO=$1"
260}
261trap 'debuglog $LINENO' DEBUG
262
263{ echo pipe1;
264 echo pipe2; } \
265 | cat
266echo ok
267
268## STDOUT:
269 LINENO=8
270pipe1
271pipe2
272 LINENO=9
273ok
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
283debuglog() {
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}
290trap 'debuglog $LINENO' DEBUG
291
292echo pipeline \
293 | cat
294echo ok
295
296## STDOUT:
297 LINENO=6
298 LINENO=7
299pipeline
300 LINENO=8
301ok
302## END
303## OK osh STDOUT:
304 LINENO=7
305pipeline
306 LINENO=8
307ok
308## END
309
310#### trap DEBUG and pipeline (lastpipe difference)
311debuglog() {
312 echo " [$@]"
313}
314trap '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.
324date | cat | wc -l
325
326date |
327 cat |
328 wc -l
329
330## STDOUT:
331 [6]
332a
333 [6]
334b
335 [8]
3362
337 [10]
338 [10]
339 [10]
3401
341 [12]
342 [13]
343 [14]
3441
345## END
346
347# Marking OK due to lastpipe execution difference
348
349## OK osh STDOUT:
350 [6]
351a
352 [6]
353b
354 [8]
3552
356 [10]
3571
358 [14]
3591
360## END
361
362#### trap DEBUG function call
363debuglog() {
364 echo " [$@]"
365}
366trap 'debuglog $LINENO' DEBUG
367
368f() {
369 local mylocal=1
370 for i in "$@"; do
371 echo i=$i
372 done
373}
374
375f A B # executes ONCE here, but does NOT go into the function call
376
377echo next
378
379f X Y
380
381echo ok
382
383## STDOUT:
384 [13]
385i=A
386i=B
387 [15]
388next
389 [17]
390i=X
391i=Y
392 [19]
393ok
394## END
395
396#### trap DEBUG case
397debuglog() {
398 echo " [$@]"
399}
400trap 'debuglog $LINENO' DEBUG
401
402name=foo.py
403
404case $name in
405 *.py)
406 echo python
407 ;;
408 *.sh)
409 echo shell
410 ;;
411esac
412echo ok
413
414## STDOUT:
415 [6]
416 [8]
417 [10]
418python
419 [16]
420ok
421## END
422
423#### trap DEBUG for each
424
425debuglog() {
426 echo " [$@]"
427}
428trap 'debuglog $LINENO' DEBUG
429
430for x in 1 2; do
431 echo x=$x
432done
433
434echo ok
435
436## STDOUT:
437 [6]
438 [7]
439x=1
440 [6]
441 [7]
442x=2
443 [10]
444ok
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]
455x=1
456 [7]
457x=2
458 [10]
459ok
460## END
461
462#### trap DEBUG for expr
463debuglog() {
464 echo " [$@]"
465}
466trap 'debuglog $LINENO' DEBUG
467
468for (( i =3 ; i < 5; ++i )); do
469 echo i=$i
470done
471
472echo ok
473
474## STDOUT:
475 [6]
476 [6]
477 [7]
478i=3
479 [6]
480 [6]
481 [7]
482i=4
483 [6]
484 [6]
485 [10]
486ok
487## END
488## N-I osh STDOUT:
489 [7]
490i=3
491 [7]
492i=4
493 [10]
494ok
495## END
496
497#### trap DEBUG if while
498debuglog() {
499 echo " [$@]"
500}
501trap 'debuglog $LINENO' DEBUG
502
503if test x = x; then
504 echo if
505fi
506
507while test x != x; do
508 echo while
509done
510
511## STDOUT:
512 [6]
513 [7]
514if
515 [10]
516## END
517
518
519#### trap RETURN
520profile() {
521 echo "profile [$@]"
522}
523g() {
524 echo --
525 echo g
526 echo --
527 return
528}
529f() {
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.
537trap 'profile x y' RETURN
538f
539. $REPO_ROOT/spec/testdata/return-helper.sh
540## status: 42
541## STDOUT:
542--
543f
544--
545--
546g
547--
548return-helper.sh
549profile [x y]
550## END
551
552#### Compare trap DEBUG vs. trap ERR
553
554# Pipelines and AndOr are problematic
555
556# THREE each
557trap 'echo dbg $LINENO' DEBUG
558
559false | false | false
560
561false || false || false
562
563! true
564
565trap - DEBUG
566
567
568# ONE EACH
569trap 'echo err $LINENO' ERR
570
571false | false | false
572
573false || false || false
574
575! true # not run
576
577echo ok
578
579## STDOUT:
580dbg 3
581dbg 3
582dbg 3
583dbg 5
584dbg 5
585dbg 5
586dbg 7
587dbg 9
588err 14
589err 16
590ok
591## END
592
593
594#### Combine DEBUG trap and USR1 trap
595
596case $SH in dash|mksh|ash) exit ;; esac
597
598trap 'false; echo $LINENO usr1' USR1
599trap 'false; echo $LINENO dbg' DEBUG
600
601sh -c "kill -USR1 $$"
602echo after=$?
603
604## STDOUT:
6056 dbg
6061 dbg
6071 dbg
6081 usr1
6097 dbg
610after=0
611## END
612
613## N-I dash/mksh/ash STDOUT:
614## END
615
616#### Combine ERR trap and USR1 trap
617
618case $SH in dash|mksh|ash) exit ;; esac
619
620trap 'false; echo $LINENO usr1' USR1
621trap 'false; echo $LINENO err' ERR
622
623sh -c "kill -USR1 $$"
624echo after=$?
625
626## STDOUT:
6271 err
6281 usr1
629after=0
630## END
631
632## N-I dash/mksh/ash STDOUT:
633## END
634
635#### Combine DEBUG trap and ERR trap
636
637case $SH in dash|mksh|ash) exit ;; esac
638
639trap 'false; echo $LINENO err' ERR
640trap 'false; echo $LINENO debug' DEBUG
641
642false
643echo after=$?
644
645## STDOUT:
6466 err
6476 debug
6486 debug
6496 debug
6506 err
6517 err
6527 debug
653after=1
654## END
655
656## N-I dash/mksh/ash STDOUT:
657## END
658
659#### trap ERR with errtrace (-E)
660# ERR trap is not inherited by shell functions, sub processes, sub commands, ... unless the -o errtrace option is set
661set -E
662trap "echo ERR" ERR
663echo 0
664false
665echo 1
666cat <( date X ; echo 2 )
667echo $( date X ; echo 3 )
668{ date X ; echo 4 ; }
669{ date X ; echo 5 ; } & wait
670( date X ; echo 6 )
671f() { date X ; echo 7 ; } ; f
672x=$( date X ; echo 8 )
673true
674## status: 0
675## STDOUT:
6760
677ERR
6781
679ERR
6802
681ERR 3
682ERR
6834
684ERR
6855
686ERR
6876
688ERR
6897
690## END
691
692#### trap ERR without errtrace (+E)
693# ERR trap is not inherited by shell functions, sub processes, sub commands, ... unless the -o errtrace option is set
694set +E
695trap "echo ERR" ERR
696echo 0
697false
698echo 1
699cat <( date X ; echo 2 )
700echo $( date X ; echo 3 )
701{ date X ; echo 4 ; }
702{ date X ; echo 5 ; } & wait
703( date X ; echo 6 )
704f() { date X ; echo 7 ; } ; f
705x=$( date X ; echo 8 )
706true
707## status: 0
708## STDOUT:
7090
710ERR
7111
7122
7133
714ERR
7154
7165
7176
7187
719## END
720
721#### trap ERR LINENO
722# ERR trap is not inherited by shell functions, sub processes, sub commands, ... unless the -o errtrace option is set
723set +E
724trap 'echo ERR=$LINENO' ERR
725echo 0
726false
727true
728## status: 0
729## STDOUT:
7300
731ERR=4
732## END
733
734#### trap ERR NoLastFork
735trap 'echo ERR' ERR ; date X
736## status: 1
737## STDOUT:
738ERR
739## END
740
741#### trap ERR shadowing with errtrace (-E)
742# ERR trap is not inherited by shell functions, sub processes, sub commands, ... unless the -o errtrace option is set
743set -E
744trap "echo ERR" ERR
745false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
746cat <( trap "echo ERR2" ERR ; date X )
747false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
748echo $( trap "echo ERR3" ERR ; date X )
749false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
750{ trap "echo ERR4" ERR ; date X ; }
751false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
752{ trap "echo ERR5" ERR ; date X ; } & wait
753false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
754( trap "echo ERR6" ERR ; date X )
755false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
756f() { trap "echo ERR7" ERR ; date X ; } ; f
757false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
758x=$( trap "echo ERR8" ERR ; date X ; echo 8 )
759true
760## status: 0
761## STDOUT:
762ERR
763ERRx
764ERR2
765ERR
766ERRx
767ERR3
768ERR
769ERRx
770ERR4
771ERR4
772ERRx
773ERR5
774ERR
775ERRx
776ERR6
777ERR
778ERR
779ERRx
780ERR7
781ERR7
782ERR7
783ERRx
784## END
785
786#### trap ERR shadowing without errtrace (+E)
787# ERR trap is not inherited by shell functions, sub processes, sub commands, ... unless the -o errtrace option is set
788set +E
789trap "echo ERR" ERR
790false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
791cat <( trap "echo ERR2" ERR ; date X )
792false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
793echo $( trap "echo ERR3" ERR ; date X )
794false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
795{ trap "echo ERR4" ERR ; date X ; }
796false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
797{ trap "echo ERR5" ERR ; date X ; } & wait
798false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
799( trap "echo ERR6" ERR ; date X )
800false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
801f() { trap "echo ERR7" ERR ; date X ; } ; f
802false ; trap "echo ERRx" ERR ; false ; trap "echo ERR" ERR
803x=$( trap "echo ERR8" ERR ; date X ; echo 8 )
804true
805## status: 0
806## STDOUT:
807ERR
808ERRx
809ERR2
810ERR
811ERRx
812ERR3
813ERR
814ERRx
815ERR4
816ERR4
817ERRx
818ERR5
819ERR
820ERRx
821ERR6
822ERR
823ERR
824ERRx
825ERR7
826ERR7
827ERR7
828ERRx
829## END