1 #!/usr/bin/env bash
2 #
3 # Test call stack introspection. There are a bunch of special variables
4 # defined here:
5 #
6 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
7 #
8 # - The shell function ${FUNCNAME[$i]} is defined in the file
9 # ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}
10 #
11 # - ${BASH_LINENO[$i]} is the line number in the source file
12 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
13 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
14 #
15 # - For instance, ${FUNCNAME[$i]} was called from the file
16 # ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin
17 # displays the current call stack using this information.
18 #
19 # So ${BASH_SOURCE[@]} doesn't line up with ${BASH_LINENO}. But
20 # ${BASH_SOURCE[0]} does line up with $LINENO!
21 #
22 # Geez.
23 #
24 # In other words, BASH_SOURCE is about the DEFINITION. While FUNCNAME and
25 # BASH_LINENO are about the CALL.
26
27
28 #### ${FUNCNAME[@]} array
29 g() {
30 argv.py "${FUNCNAME[@]}"
31 }
32 f() {
33 argv.py "${FUNCNAME[@]}"
34 g
35 argv.py "${FUNCNAME[@]}"
36 }
37 f
38 ## STDOUT:
39 ['f']
40 ['g', 'f']
41 ['f']
42 ## END
43
44 #### FUNCNAME with source
45 f() {
46 . spec/testdata/echo-funcname.sh
47 }
48 g() {
49 f
50 }
51 g
52 . spec/testdata/echo-funcname.sh
53 argv.py "${FUNCNAME[@]}"
54 ## STDOUT:
55 ['source', 'f', 'g']
56 ['source']
57 []
58 ## END
59
60
61 #### ${BASH_SOURCE} usable as a string (e.g. for virtualenv)
62
63 # https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate.sh
64
65 argv.py "$BASH_SOURCE" # SimpleVarSUb
66 argv.py "${BASH_SOURCE}" # BracedVarSub
67 source spec/testdata/bash-source-string.sh
68
69 ## STDOUT:
70 ['']
71 ['']
72 ['spec/testdata/bash-source-string.sh']
73 ['spec/testdata/bash-source-string.sh']
74 ['spec/testdata/bash-source-string2.sh']
75 ['spec/testdata/bash-source-string2.sh']
76 ## END
77
78 #### ${BASH_SOURCE[@]} with source and function name
79 argv.py "${BASH_SOURCE[@]}"
80 source spec/testdata/bash-source-simple.sh
81 f
82 ## STDOUT:
83 []
84 ['spec/testdata/bash-source-simple.sh']
85 ['spec/testdata/bash-source-simple.sh']
86 ## END
87
88 #### ${BASH_SOURCE[@]} with line numbers
89 $SH spec/testdata/bash-source.sh
90 ## STDOUT:
91 ['begin F funcs', 'f', 'main']
92 ['begin F files', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
93 ['begin F lines', '21', '0']
94 ['G funcs', 'g', 'f', 'main']
95 ['G files', 'spec/testdata/bash-source-2.sh', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
96 ['G lines', '15', '21', '0']
97 ['end F funcs', 'f', 'main']
98 ['end F', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
99 ['end F lines', '21', '0']
100 ## END
101
102 #### ${BASH_LINENO[@]} is a stack of line numbers for function calls
103 # note: it's CALLS, not DEFINITIONS.
104 g() {
105 argv.py G "${BASH_LINENO[@]}"
106 }
107 f() {
108 argv.py 'begin F' "${BASH_LINENO[@]}"
109 g # line 6
110 argv.py 'end F' "${BASH_LINENO[@]}"
111 }
112 argv.py ${BASH_LINENO[@]}
113 f # line 9
114 ## STDOUT:
115 []
116 ['begin F', '10']
117 ['G', '6', '10']
118 ['end F', '10']
119 ## END