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