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

647 lines, 326 significant
1## oils_failures_allowed: 2
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
17trap 'echo line=$LINENO' ERR
18
19false
20false
21echo ok
22
23## STDOUT:
24line=3
25line=4
26ok
27## END
28
29#### trap ERR and set -o errexit
30
31trap 'echo line=$LINENO' ERR
32
33false
34echo a
35
36set -o errexit
37
38echo b
39false # trap executed, and executation also halts
40echo c # doesn't get here
41
42## status: 1
43## STDOUT:
44line=3
45a
46b
47line=9
48## END
49
50#### trap ERR and errexit disabled context
51
52trap 'echo line=$LINENO' ERR
53
54false
55echo a
56
57set -o errexit
58
59echo b
60if false; then
61 echo xx
62fi
63echo c # doesn't get here
64
65## STDOUT:
66line=3
67a
68b
69c
70## END
71
72#### trap ERR and if statement
73
74if test -f /nope; then echo file exists; fi
75
76trap 'echo err' ERR
77#trap 'echo line=$LINENO' ERR
78
79if test -f /nope; then echo file exists; fi
80
81## STDOUT:
82## END
83
84
85#### trap ERR and || conditional
86
87trap 'echo line=$LINENO' ERR
88
89false || false || false
90echo ok
91
92false && false
93echo ok
94
95## STDOUT:
96line=3
97ok
98ok
99## END
100
101#### trap ERR and pipeline
102
103# mksh and bash have different line numbers in this case
104#trap 'echo line=$LINENO' ERR
105trap 'echo line=$LINENO' ERR
106
107# it's run for the last 'false'
108false | false | false
109
110{ echo pipeline; false; } | false | false
111
112# it's never run here
113! true
114! false
115
116## STDOUT:
117line=3
118line=5
119## END
120
121## BUG mksh/ash STDOUT:
122line=1
123line=1
124## END
125
126
127#### trap ERR pipelines without simple commands
128
129trap 'echo assign' ERR
130a=$(false) | a=$(false) | a=$(false)
131
132trap 'echo dparen' ERR
133(( 0 )) | (( 0 )) | (( 0 ))
134
135trap 'echo dbracket' ERR
136[[ a = b ]] | [[ a = b ]] | [[ a = b ]]
137
138# bash anomaly - it gets printed twice?
139trap 'echo subshell' ERR
140(false) | (false) | (false) | (false)
141
142# same bug
143trap 'echo subshell2' ERR
144(false) | (false) | (false) | (false; false)
145
146trap 'echo group' ERR
147{ false; } | { false; } | { false; }
148
149echo ok
150
151## STDOUT:
152assign
153dparen
154dbracket
155subshell
156subshell2
157group
158ok
159## END
160
161## BUG bash STDOUT:
162assign
163dparen
164dbracket
165subshell
166subshell
167subshell2
168subshell2
169group
170ok
171## END
172
173
174#### Pipeline group quirk
175
176# Oh this is because it's run for the PIPELINE, not for the last thing! Hmmm
177
178trap 'echo group2' ERR
179{ false; } | { false; } | { false; false; }
180
181echo ok
182
183## STDOUT:
184group2
185ok
186## END
187
188#### trap ERR does not run in errexit situations
189
190trap 'echo line=$LINENO' ERR
191
192if false; then
193 echo if
194fi
195
196while false; do
197 echo while
198done
199
200until false; do
201 echo until
202 break
203done
204
205false || false || false
206
207false && false && false
208
209false; false; false
210
211echo ok
212
213## STDOUT:
214until
215line=16
216line=20
217line=20
218line=20
219ok
220## END
221
222
223#### trap ERR doesn't run in subprograms - subshell, command sub, async
224
225trap 'echo line=$LINENO' ERR
226
227( false; echo subshell )
228
229x=$( false; echo command sub )
230
231false & wait
232
233{ false; echo async; } & wait
234
235false
236echo ok
237
238## STDOUT:
239subshell
240async
241line=11
242ok
243## END
244
245#### set -o errtrace: trap ERR runs in subprograms
246case $SH in mksh) exit ;; esac
247
248set -o errtrace
249trap 'echo line=$LINENO' ERR
250
251( false; echo subshell )
252
253x=$( false; echo command sub )
254
255false
256echo ok
257
258## STDOUT:
259line=6
260subshell
261line=10
262ok
263## END
264
265# ash doesn't reject errtrace, but doesn't implement it
266## BUG ash STDOUT:
267subshell
268line=10
269ok
270## END
271
272## N-I mksh STDOUT:
273## END
274
275#### trap ERR doesn't run with &
276
277trap 'echo line=$LINENO' ERR
278
279false & wait
280
281{ false; echo async; } & wait
282
283## STDOUT:
284async
285## END
286
287
288#### set -o errtrace: trap ERR with &
289case $SH in mksh) exit ;; esac
290
291set -o errtrace
292trap 'echo line=$LINENO' ERR
293
294false & wait
295
296{ false; echo async; } & wait
297
298## STDOUT:
299line=8
300async
301## END
302
303## BUG ash STDOUT:
304async
305## END
306
307## N-I mksh STDOUT:
308## END
309
310
311
312#### trap ERR not active in shell functions in (bash behavior)
313
314trap 'echo line=$LINENO' ERR
315
316f() {
317 false
318 true
319}
320
321f
322
323## STDOUT:
324## END
325
326## N-I mksh STDOUT:
327line=4
328## END
329
330#### set -o errtrace - trap ERR runs in shell functions
331
332trap 'echo err' ERR
333
334passing() {
335 false # line 4
336 true
337}
338
339failing() {
340 true
341 false
342}
343
344passing
345failing
346
347set -o errtrace
348
349echo 'now with errtrace'
350passing
351failing
352
353echo ok
354
355## STDOUT:
356err
357now with errtrace
358err
359err
360err
361ok
362## END
363
364## BUG mksh status: 1
365## BUG mksh STDOUT:
366err
367err
368## END
369
370#### set -o errtrace - trap ERR runs in shell functions (LINENO)
371
372trap 'echo line=$LINENO' ERR
373
374passing() {
375 false # line 4
376 true
377}
378
379failing() {
380 true
381 false
382}
383
384passing
385failing
386
387set -o errtrace
388
389echo 'now with errtrace'
390passing
391failing
392
393echo ok
394
395## STDOUT:
396line=14
397now with errtrace
398line=4
399line=10
400line=20
401ok
402## END
403
404## BUG mksh status: 1
405## BUG mksh STDOUT:
406line=4
407line=10
408## END
409
410#### trap ERR with "atoms": assignment (( [[
411
412trap 'echo line=$LINENO' ERR
413
414x=$(false)
415
416[[ a == b ]]
417
418(( 0 ))
419echo ok
420
421## STDOUT:
422line=3
423line=5
424line=7
425ok
426## END
427
428## BUG mksh STDOUT:
429line=3
430line=3
431line=7
432ok
433## END
434
435
436#### trap ERR with for, case, { }
437
438trap 'echo line=$LINENO' ERR
439
440for y in 1 2; do
441 false
442done
443
444case x in
445 x) false ;;
446 *) false ;;
447esac
448
449{ false; false; false; }
450echo ok
451
452## STDOUT:
453line=4
454line=4
455line=8
456line=12
457line=12
458line=12
459ok
460## END
461
462#### trap ERR with redirect
463
464trap 'echo line=$LINENO' ERR
465
466false
467
468{ false
469 true
470} > /zz # error
471echo ok
472
473## STDOUT:
474line=3
475line=7
476ok
477## END
478
479# doesn't update line for redirect
480
481## BUG bash/mksh STDOUT:
482line=3
483line=3
484ok
485## END
486
487## BUG ash STDOUT:
488line=3
489ok
490## END
491
492
493#### trap ERR with YSH proc
494
495case $SH in bash|mksh|ash) exit ;; esac
496
497# seems the same
498
499shopt -s ysh:upgrade
500
501proc handler {
502 echo err
503}
504
505if test -f /nope { echo file exists }
506
507trap handler ERR
508
509if test -f /nope { echo file exists }
510
511false || true # not run for the first part here
512false
513
514## status: 1
515## STDOUT:
516err
517## END
518
519## N-I bash/mksh/ash status: 0
520## N-I bash/mksh/ash STDOUT:
521## END
522
523#### trap ERR
524err() {
525 echo "err [$@] $?"
526}
527trap 'err x y' ERR
528
529echo A
530
531false
532echo B
533
534( exit 42 )
535echo C
536
537trap - ERR # disable trap
538
539false
540echo D
541
542trap 'echo after errexit $?' ERR
543
544set -o errexit
545
546( exit 99 )
547echo E
548
549## status: 99
550## STDOUT:
551A
552err [x y] 1
553B
554err [x y] 42
555C
556D
557after errexit 99
558## END
559## N-I dash STDOUT:
560A
561B
562C
563D
564## END
565
566#### trap ERR and pipelines - PIPESTATUS difference
567case $SH in ash) exit ;; esac
568
569err() {
570 echo "err [$@] status=$? [${PIPESTATUS[@]}]"
571}
572trap 'err' ERR
573
574echo A
575
576false
577
578# succeeds
579echo B | grep B
580
581# fails
582echo C | grep zzz
583
584echo D | grep zzz | cat
585
586set -o pipefail
587echo E | grep zzz | cat
588
589trap - ERR # disable trap
590
591echo F | grep zz
592echo ok
593
594## STDOUT:
595A
596err [] status=1 [1]
597B
598err [] status=1 [0 1]
599err [] status=1 [0 1 0]
600ok
601## END
602
603# we don't set PIPESTATUS unless we get a pipeline
604
605## OK osh STDOUT:
606A
607err [] status=1 []
608B
609err [] status=1 [0 1]
610err [] status=1 [0 1 0]
611ok
612## END
613
614## N-I ash STDOUT:
615## END
616
617#### error in trap ERR (recursive)
618case $SH in dash) exit ;; esac
619
620err() {
621 echo err status $?
622 false
623 ( exit 2 ) # not recursively triggered
624 echo err 2
625}
626trap 'err' ERR
627
628echo A
629false
630echo B
631
632# Try it with errexit
633set -e
634false
635echo C
636
637## status: 1
638## STDOUT:
639A
640err status 1
641err 2
642B
643err status 1
644## END
645## N-I dash STDOUT:
646## END
647