1 ## compare_shells: 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 (scalar or array)
45 cd $REPO_ROOT
46
47 # Comments on bash quirk:
48 # https://github.com/oilshell/oil/pull/656#issuecomment-599162211
49
50 f() {
51 . spec/testdata/echo-funcname.sh
52 }
53 g() {
54 f
55 }
56
57 g
58 echo -----
59
60 . spec/testdata/echo-funcname.sh
61 echo -----
62
63 argv.py "${FUNCNAME[@]}"
64
65 # Show bash inconsistency. FUNCNAME doesn't behave like a normal array.
66 case $SH in
67 (bash)
68 echo -----
69 a=('A')
70 argv.py ' @' "${a[@]}"
71 argv.py ' 0' "${a[0]}"
72 argv.py '${}' "${a}"
73 argv.py ' $' "$a"
74 ;;
75 esac
76
77 ## STDOUT:
78 [' @', 'source', 'f', 'g']
79 [' 0', 'source']
80 ['${}', 'source']
81 [' $', 'source']
82 -----
83 [' @', 'source']
84 [' 0', 'source']
85 ['${}', 'source']
86 [' $', 'source']
87 -----
88 []
89 ## END
90 ## BUG bash STDOUT:
91 [' @', 'source', 'f', 'g']
92 [' 0', 'source']
93 ['${}', 'source']
94 [' $', 'source']
95 -----
96 [' @']
97 [' 0', '']
98 ['${}', '']
99 [' $', '']
100 -----
101 []
102 -----
103 [' @', 'A']
104 [' 0', 'A']
105 ['${}', 'A']
106 [' $', 'A']
107 ## END
108
109
110 #### BASH_SOURCE and BASH_LINENO scalar or array (e.g. for virtualenv)
111 cd $REPO_ROOT
112
113 # https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate.sh
114 # https://github.com/akinomyoga/ble.sh/blob/6f6c2e5/ble.pp#L374
115
116 argv.py "$BASH_SOURCE" # SimpleVarSub
117 argv.py "${BASH_SOURCE}" # BracedVarSub
118 argv.py "$BASH_LINENO" # SimpleVarSub
119 argv.py "${BASH_LINENO}" # BracedVarSub
120 argv.py "$FUNCNAME" # SimpleVarSub
121 argv.py "${FUNCNAME}" # BracedVarSub
122 echo __
123 source spec/testdata/bash-source-string.sh
124
125 ## STDOUT:
126 ['']
127 ['']
128 ['']
129 ['']
130 ['']
131 ['']
132 __
133 ['spec/testdata/bash-source-string.sh']
134 ['spec/testdata/bash-source-string.sh']
135 ['11']
136 ['11']
137 ____
138 ['spec/testdata/bash-source-string2.sh']
139 ['spec/testdata/bash-source-string2.sh']
140 ['11']
141 ['11']
142 ## END
143
144
145 #### ${FUNCNAME} with prefix/suffix operators
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
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: 0
168 ## STDOUT:
169 ['check']
170 ['5']
171 ['c']
172 ['heck']
173 ## END
174
175 #### ${FUNCNAME} and "set -u" (OSH regression)
176 set -u
177 argv.py "$FUNCNAME"
178 ## status: 1
179 ## stdout-json: ""
180
181 #### $((BASH_LINENO)) (scalar form in arith)
182 check() {
183 echo $((BASH_LINENO))
184 }
185 check
186 ## stdout: 4
187
188 #### ${BASH_SOURCE[@]} with source and function name
189 cd $REPO_ROOT
190
191 argv.py "${BASH_SOURCE[@]}"
192 source spec/testdata/bash-source-simple.sh
193 f
194 ## STDOUT:
195 []
196 ['spec/testdata/bash-source-simple.sh']
197 ['spec/testdata/bash-source-simple.sh']
198 ## END
199
200 #### ${BASH_SOURCE[@]} with line numbers
201 cd $REPO_ROOT
202
203 $SH spec/testdata/bash-source.sh
204 ## STDOUT:
205 ['begin F funcs', 'f', 'main']
206 ['begin F files', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
207 ['begin F lines', '21', '0']
208 ['G funcs', 'g', 'f', 'main']
209 ['G files', 'spec/testdata/bash-source-2.sh', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
210 ['G lines', '15', '21', '0']
211 ['end F funcs', 'f', 'main']
212 ['end F', 'spec/testdata/bash-source.sh', 'spec/testdata/bash-source.sh']
213 ['end F lines', '21', '0']
214 ## END
215
216 #### ${BASH_LINENO[@]} is a stack of line numbers for function calls
217 # note: it's CALLS, not DEFINITIONS.
218 g() {
219 argv.py G "${BASH_LINENO[@]}"
220 }
221 f() {
222 argv.py 'begin F' "${BASH_LINENO[@]}"
223 g # line 6
224 argv.py 'end F' "${BASH_LINENO[@]}"
225 }
226 argv.py ${BASH_LINENO[@]}
227 f # line 9
228 ## STDOUT:
229 []
230 ['begin F', '10']
231 ['G', '6', '10']
232 ['end F', '10']
233 ## END
234
235 #### Locations with temp frame
236
237 cd $REPO_ROOT
238
239 $SH spec/testdata/bash-source-pushtemp.sh
240
241 ## STDOUT:
242 F
243 G
244 STACK:spec/testdata/bash-source-pushtemp.sh:g:3
245 STACK:spec/testdata/bash-source-pushtemp.sh:f:19
246 STACK:spec/testdata/bash-source-pushtemp.sh:main:0
247 ## END
248
249 #### Locations when sourcing
250
251 cd $REPO_ROOT
252
253 # like above test case, but we source
254
255 # bash location doesn't make sense:
256 # - It says 'source' happens at line 1 of bash-source-pushtemp. Well I think
257 # - It really happens at line 2 of '-c' ! I guess that's to line up
258 # with the 'main' frame
259
260 $SH -c 'true;
261 source spec/testdata/bash-source-pushtemp.sh'
262
263 ## STDOUT:
264 F
265 G
266 STACK:spec/testdata/bash-source-pushtemp.sh:g:3
267 STACK:spec/testdata/bash-source-pushtemp.sh:f:19
268 STACK:spec/testdata/bash-source-pushtemp.sh:source:2
269 ## END
270
271 #### Sourcing inside function grows the debug stack
272
273 cd $REPO_ROOT
274
275 $SH spec/testdata/bash-source-source.sh
276
277 ## STDOUT:
278 F
279 G
280 STACK:spec/testdata/bash-source-pushtemp.sh:g:3
281 STACK:spec/testdata/bash-source-pushtemp.sh:f:19
282 STACK:spec/testdata/bash-source-pushtemp.sh:source:2
283 STACK:spec/testdata/bash-source-source.sh:mainfunc:6
284 STACK:spec/testdata/bash-source-source.sh:main2:10
285 STACK:spec/testdata/bash-source-source.sh:main1:13
286 STACK:spec/testdata/bash-source-source.sh:main:0
287 ## END