It can make for some pretty excellent stack traces from complicated (even multi-file) bash-based programs; e.g.,
$ ./track.sh
I failed!
at make_failure (file "common.sh" line 3)
at d (file "./track.sh" line 43)
at c (file "./track.sh" line 38)
at b (file "./track.sh" line 33)
at a (file "./track.sh" line 28)
at main (file "./track.sh" line 54)
From the source files:
$ nl -ba < common.sh
1 function make_failure {
2 echo "I failed!"
3 false
4 }
$ nl -ba < track.sh
1 #!/bin/bash
2
3 . "common.sh"
4
5 function stack_trace
6 {
7 set +o xtrace
8 set +o errexit
9 set +o errtrace
10 trap '' ERR
11
12 for (( i = 0; i < ${#FUNCNAME[@]}; i++ )); do
13 if [[ "${FUNCNAME[i]}" == stack_trace ]]; then
14 continue
15 fi
16 if (( i > 0 )); then
17 line="${BASH_LINENO[$((i - 1))]}"
18 else
19 line="${LINENO}"
20 fi
21 printf ' at %s (file "%s" line %d)\n' "${FUNCNAME[i]}" \
22 "${BASH_SOURCE[i]}" "${line}"
23 done
24 }
25
26 function a
27 {
28 b
29 }
30
31 function b
32 {
33 c
34 }
35
36 function c
37 {
38 d
39 }
40
41 function d
42 {
43 make_failure
44 }
45
46 #
47 # Install our error handling trap, so that we can have stack traces on
48 # failures. We set "errtrace" so that the ERR trap handler is inherited
49 # by each function call.
50 #
51 trap stack_trace ERR
52 set -o errtrace
53
54 a
In Zsh, TMOUT works as well but differently from Bash (which exits with status 0) the shell exits with error status code 14 (the numeric value of SIGALRM). However that can be overridden with a trap function – for example:
TRAPALRM() {
echo 'zsh: logging out after timeout'
exit 0
}
FUNCNAME is another good one.
When you need it, you’ll be glad it’s there.
It can make for some pretty excellent stack traces from complicated (even multi-file) bash-based programs; e.g.,
From the source files:
You won’t believe number four!
In Zsh,
TMOUT
works as well but differently from Bash (which exits with status 0) the shell exits with error status code 14 (the numeric value of SIGALRM). However that can be overridden with a trap function – for example: