| 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 |