1 #
2 # Test call stack introspection. There are a bunch of special variables
3 # defined here:
4 #
5 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
6 #
7 # - The shell function ${FUNCNAME[$i]} is defined in the file
8 # ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}
9 #
10 # - ${BASH_LINENO[$i]} is the line number in the source file
11 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
12 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
13 #
14 # - For instance, ${FUNCNAME[$i]} was called from the file
15 # ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin
16 # displays the current call stack using this information.
17 #
18 # So ${BASH_SOURCE[@]} doesn't line up with ${BASH_LINENO}. But
19 # ${BASH_SOURCE[0]} does line up with $LINENO!
20 #
21 # Geez.
22 #
23 # In other words, BASH_SOURCE is about the DEFINITION. While FUNCNAME and
24 # BASH_LINENO are about the CALL.
25
26
27 #### ${FUNCNAME[@]} array
28 g() {
29 argv.py "${FUNCNAME[@]}"
30 }
31 f() {
32 argv.py "${FUNCNAME[@]}"
33 g
34 argv.py "${FUNCNAME[@]}"
35 }
36 f
37 ## STDOUT:
38 ['f']
39 ['g', 'f']
40 ['f']
41 ## END
42
43 #### FUNCNAME with source (scalar or array)
44 cd $REPO_ROOT
45
46 # Comments on bash quirk:
47 # https://github.com/oilshell/oil/pull/656#issuecomment-599162211
48
49 f() {
50 . spec/testdata/echo-funcname.sh
51 }
52 g() {
53 f
54 }
55
56 g
57 echo -----
58
59 . spec/testdata/echo-funcname.sh
60 echo -----
61
62 argv.py "${FUNCNAME[@]}"
63
64 # Show bash inconsistency. FUNCNAME doesn't behave like a normal array.
65 case $SH in
66 (bash)
67 echo -----
68 a=('A')
69 argv.py ' @' "${a[@]}"
70 argv.py ' 0' "${a[0]}"
71 argv.py '${}' "${a}"
72 argv.py ' $' "$a"
73 ;;
74 esac
75
76 ## STDOUT:
77 [' @', 'source', 'f', 'g']
78 [' 0', 'source']
79 ['${}', 'source']
80 [' $', 'source']
81 -----
82 [' @', 'source']
83 [' 0', 'source']
84 ['${}', 'source']
85 [' $', 'source']
86 -----
87 []
88 ## END
89 ## BUG bash STDOUT:
90 [' @', 'source', 'f', 'g']
91 [' 0', 'source']
92 ['${}', 'source']
93 [' $', 'source']
94 -----
95 [' @', 'source']
96 [' 0', 'source']
97 ['${}', '']
98 [' $', '']
99 -----
100 []
101 -----
102 [' @', 'A']
103 [' 0', 'A']
104 ['${}', 'A']
105 [' $', 'A']
106 ## END
107
108
109 #### BASH_SOURCE and BASH_LINENO scalar or array (e.g. for virtualenv)
110 cd $REPO_ROOT
111
112 # https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate.sh
113 # https://github.com/akinomyoga/ble.sh/blob/6f6c2e5/ble.pp#L374
114
115 argv.py "$BASH_SOURCE" # SimpleVarSub
116 argv.py "${BASH_SOURCE}" # BracedVarSub
117 argv.py "$BASH_LINENO" # SimpleVarSub
118 argv.py "${BASH_LINENO}" # BracedVarSub
119 argv.py "$FUNCNAME" # SimpleVarSub
120 argv.py "${FUNCNAME}" # BracedVarSub
121 echo __
122 source spec/testdata/bash-source-string.sh
123
124 ## STDOUT:
125 ['']
126 ['']
127 ['']
128 ['']
129 ['']
130 ['']
131 __
132 ['spec/testdata/bash-source-string.sh']
133 ['spec/testdata/bash-source-string.sh']
134 ['11']
135 ['11']
136 ____
137 ['spec/testdata/bash-source-string2.sh']
138 ['spec/testdata/bash-source-string2.sh']
139 ['11']
140 ['11']
141 ## END
142
143
144 #### ${FUNCNAME} with prefix/suffix operators
145 shopt -s compat_array
146
147 check() {
148 argv.py "${#FUNCNAME}"
149 argv.py "${FUNCNAME::1}"
150 argv.py "${FUNCNAME:1}"
151 }
152 check
153 ## STDOUT:
154 ['5']
155 ['c']
156 ['heck']
157 ## END
158
159 #### operators on FUNCNAME not allowed by default
160 check() {
161 argv.py "${FUNCNAME}"
162 argv.py "${#FUNCNAME}"
163 argv.py "${FUNCNAME::1}"
164 argv.py "${FUNCNAME:1}"
165 }
166 check
167 ## status: 1
168 ## STDOUT:
169 ['check']
170 ## END
171 ## OK bash status: 0
172 ## OK bash STDOUT:
173 ['check']
174 ['5']
175 ['c']
176 ['heck']
177 ## END
178
179 #### ${FUNCNAME} and "set -u" (OSH regression)
180 set -u
181 argv.py "$FUNCNAME"
182 ## status: 1
183 ## stdout-json: ""
184
185 #### $((BASH_LINENO)) (scalar form in arith)
186 check() {
187 echo $((BASH_LINENO))
188 }
189 check
190 ## stdout: 4
191
192 #### ${BASH_SOURCE[@]} with source and function name
193 cd $REPO_ROOT
194
195 argv.py "${BASH_SOURCE[@]}"
196 source spec/testdata/bash-source-simple.sh
197 f
198 ## STDOUT:
199 []
200 ['spec/testdata/bash-source-simple.sh']
201 ['spec/testdata/bash-source-simple.sh']
202 ## END
203
204 #### ${BASH_SOURCE[@]} with line numbers
205 cd $REPO_ROOT
206
207 $SH spec/testdata/bash-source.sh
208 ## STDOUT:
209 ['begin F funcs', 'f', 'main']
210 ['begin F files', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
211 ['begin F lines', '21', '0']
212 ['G funcs', 'g', 'f', 'main']
213 ['G files', 'spec/testdata/bash-source-2.sh', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
214 ['G lines', '15', '21', '0']
215 ['end F funcs', 'f', 'main']
216 ['end F', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
217 ['end F lines', '21', '0']
218 ## END
219
220 #### ${BASH_LINENO[@]} is a stack of line numbers for function calls
221 # note: it's CALLS, not DEFINITIONS.
222 g() {
223 argv.py G "${BASH_LINENO[@]}"
224 }
225 f() {
226 argv.py 'begin F' "${BASH_LINENO[@]}"
227 g # line 6
228 argv.py 'end F' "${BASH_LINENO[@]}"
229 }
230 argv.py ${BASH_LINENO[@]}
231 f # line 9
232 ## STDOUT:
233 []
234 ['begin F', '10']
235 ['G', '6', '10']
236 ['end F', '10']
237 ## END