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
|