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 |