| 1 | # |
| 2 | # Var refs are done with ${!a} |
| 3 | # |
| 4 | # local/declare -n is tested in spec/named-ref.test.sh. |
| 5 | # |
| 6 | # http://stackoverflow.com/questions/16461656/bash-how-to-pass-array-as-an-argument-to-a-function |
| 7 | |
| 8 | #### var ref ${!a} |
| 9 | a=b |
| 10 | b=c |
| 11 | echo ref ${!a} ${a} |
| 12 | ## stdout: ref c b |
| 13 | |
| 14 | #### ${!ref-default} |
| 15 | ref=x |
| 16 | echo x=${!ref-default} |
| 17 | |
| 18 | x='' |
| 19 | echo x=${!ref-default} |
| 20 | |
| 21 | x=foo |
| 22 | echo x=${!ref-default} |
| 23 | |
| 24 | ## STDOUT: |
| 25 | x=default |
| 26 | x= |
| 27 | x=foo |
| 28 | ## END |
| 29 | |
| 30 | #### ${!undef:-} |
| 31 | # bash gives empty string, but I feel like this could be an error |
| 32 | echo undef=${!undef-'default'} |
| 33 | echo undef=${!undef} |
| 34 | |
| 35 | set -u |
| 36 | echo NOUNSET |
| 37 | echo undef=${!undef-'default'} |
| 38 | echo undef=${!undef} |
| 39 | |
| 40 | ## status: 1 |
| 41 | ## STDOUT: |
| 42 | undef=default |
| 43 | undef= |
| 44 | NOUNSET |
| 45 | undef=default |
| 46 | ## END |
| 47 | |
| 48 | #### comparison to ${!array[@]} keys (similar SYNTAX) |
| 49 | shopt --set compat_array # bypass errors on ${!a} and ${!A} |
| 50 | |
| 51 | declare -a a=(x y) |
| 52 | argv.py "${!a[@]}" |
| 53 | echo a_keys=$? |
| 54 | |
| 55 | argv.py "${!a}" # missing [] is equivalent to ${!a[0]} ? |
| 56 | echo a_nobrackets=$? |
| 57 | |
| 58 | echo --- |
| 59 | declare -A A=([A]=a [B]=b) |
| 60 | |
| 61 | argv.py ${!A[@]} |
| 62 | echo A_keys=$? |
| 63 | |
| 64 | argv.py "${!A}" # missing [] is equivalent to ${!A[0]} ? |
| 65 | echo A_nobrackets=$? |
| 66 | |
| 67 | ## STDOUT: |
| 68 | ['0', '1'] |
| 69 | a_keys=0 |
| 70 | [''] |
| 71 | a_nobrackets=0 |
| 72 | --- |
| 73 | ['A', 'B'] |
| 74 | A_keys=0 |
| 75 | [''] |
| 76 | A_nobrackets=0 |
| 77 | ## END |
| 78 | |
| 79 | #### ${!a[@]-'default'} is illegal |
| 80 | |
| 81 | # bash disallows this when a is an array. We make it an error because [@] |
| 82 | # implies it's an array. |
| 83 | |
| 84 | argv.py "${!a[@]-default}" |
| 85 | echo status=$? |
| 86 | |
| 87 | a=(x y z) |
| 88 | argv.py "${!a[@]-default}" |
| 89 | echo status=$? |
| 90 | ## status: 1 |
| 91 | ## STDOUT: |
| 92 | ## END |
| 93 | ## BUG bash status: 0 |
| 94 | ## BUG bash STDOUT: |
| 95 | ['default'] |
| 96 | status=0 |
| 97 | status=1 |
| 98 | ## END |
| 99 | |
| 100 | |
| 101 | #### var ref to $@ with @ |
| 102 | set -- one two |
| 103 | ref='@' |
| 104 | echo ref=${!ref} |
| 105 | ## STDOUT: |
| 106 | ref=one two |
| 107 | ## END |
| 108 | |
| 109 | #### var ref to $1 and $2 with 1 and 2 |
| 110 | set -- one two |
| 111 | ref1='1' |
| 112 | echo ref1=${!ref1} |
| 113 | ref2='2' |
| 114 | echo ref2=${!ref2} |
| 115 | |
| 116 | ## STDOUT: |
| 117 | ref1=one |
| 118 | ref2=two |
| 119 | ## END |
| 120 | |
| 121 | #### var ref: 1, @, * |
| 122 | set -- x y |
| 123 | ref=1; argv.py "${!ref}" |
| 124 | ref=@; argv.py "${!ref}" |
| 125 | ref=*; argv.py "${!ref}" # maybe_decay_array bug? |
| 126 | |
| 127 | ## STDOUT: |
| 128 | ['x'] |
| 129 | ['x', 'y'] |
| 130 | ['x y'] |
| 131 | ## END |
| 132 | |
| 133 | #### var ref to special var BASH_SOURCE |
| 134 | ref='LINENO' |
| 135 | echo lineno=${!ref} |
| 136 | ## STDOUT: |
| 137 | lineno=2 |
| 138 | ## END |
| 139 | |
| 140 | #### var ref to $? with '?' |
| 141 | myfunc() { |
| 142 | local ref=$1 |
| 143 | echo ${!ref} |
| 144 | } |
| 145 | myfunc FUNCNAME |
| 146 | myfunc '?' |
| 147 | ## STDOUT: |
| 148 | myfunc |
| 149 | 0 |
| 150 | ## END |
| 151 | |
| 152 | |
| 153 | #### Var ref, then assignment with ${ := } |
| 154 | z=zz |
| 155 | zz= |
| 156 | echo ${!z:=foo} |
| 157 | echo ${!z:=bar} |
| 158 | ## STDOUT: |
| 159 | foo |
| 160 | foo |
| 161 | ## END |
| 162 | |
| 163 | #### Var ref, then error with ${ ? } |
| 164 | w=ww |
| 165 | ww= |
| 166 | echo ${!w:?'my message'} |
| 167 | echo done |
| 168 | ## status: 1 |
| 169 | ## STDOUT: |
| 170 | ## END |
| 171 | |
| 172 | #### Indirect expansion, THEN suffix operators |
| 173 | shopt -s compat_array |
| 174 | |
| 175 | check_eq() { |
| 176 | [ "$1" = "$2" ] || { echo "$1 vs $2"; } |
| 177 | } |
| 178 | check_expand() { |
| 179 | val=$(eval "echo \"$1\"") |
| 180 | [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; } |
| 181 | } |
| 182 | check_err() { |
| 183 | e="$1" |
| 184 | msg=$(eval "$e" 2>&1) && echo "bad success: $e" |
| 185 | if test -n "$2"; then |
| 186 | if [[ "$msg" != $2 ]]; then |
| 187 | echo "Expected error: $e" |
| 188 | echo "Got error : $msg" |
| 189 | fi |
| 190 | fi |
| 191 | } |
| 192 | # Nearly everything in manual section 3.5.3 "Shell Parameter Expansion" |
| 193 | # is allowed after a !-indirection. |
| 194 | # |
| 195 | # Not allowed: any further prefix syntax. |
| 196 | x=xx; xx=aaabcc |
| 197 | xd=x |
| 198 | check_err '${!!xd}' |
| 199 | check_err '${!!x*}' |
| 200 | a=(asdf x) |
| 201 | check_err '${!!a[*]}' |
| 202 | check_err '${!#x}' |
| 203 | check_err '${!#a[@]}' |
| 204 | # And an array reference binds tighter in the syntax, so goes first; |
| 205 | # there's no way to spell "indirection, then array reference". |
| 206 | check_expand '${!a[1]}' xx |
| 207 | b=(aoeu a) |
| 208 | check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1] |
| 209 | # |
| 210 | # Allowed: apparently everything else. |
| 211 | y=yy; yy= |
| 212 | check_expand '${!y:-foo}' foo |
| 213 | check_expand '${!x:-foo}' aaabcc |
| 214 | |
| 215 | check_expand '${!x:?oops}' aaabcc |
| 216 | |
| 217 | check_expand '${!y:+foo}' '' |
| 218 | check_expand '${!x:+foo}' foo |
| 219 | |
| 220 | check_expand '${!x:2}' abcc |
| 221 | check_expand '${!x:2:2}' ab |
| 222 | |
| 223 | check_expand '${!x#*a}' aabcc |
| 224 | check_expand '${!x%%c*}' aaab |
| 225 | check_expand '${!x/a*b/d}' dcc |
| 226 | |
| 227 | # ^ operator not fully implemented in OSH |
| 228 | #check_expand '${!x^a}' Aaabcc |
| 229 | |
| 230 | p=pp; pp='\$ ' |
| 231 | check_expand '${!p@P}' '$ ' |
| 232 | echo ok |
| 233 | ## stdout: ok |
| 234 | |
| 235 | #### var ref OF array var |
| 236 | declare -a badref=(ale bean) |
| 237 | echo bad=${!badref} |
| 238 | ## status: 1 |
| 239 | ## STDOUT: |
| 240 | ## END |
| 241 | ## OK bash status: 0 |
| 242 | ## OK bash stdout: bad= |
| 243 | |
| 244 | #### array ref without eval_unsafe_arith |
| 245 | shopt -s compat_array |
| 246 | |
| 247 | declare -a array=(ale bean) |
| 248 | ref='array[0]' |
| 249 | echo ${!ref} |
| 250 | ## status: 1 |
| 251 | ## stdout-json: "" |
| 252 | ## N-I bash status: 0 |
| 253 | ## N-I bash STDOUT: |
| 254 | ale |
| 255 | ## END |
| 256 | |
| 257 | #### array ref without compat_array |
| 258 | shopt -s eval_unsafe_arith |
| 259 | |
| 260 | declare -a array=(ale bean) |
| 261 | ref='array' |
| 262 | echo ${!ref} |
| 263 | ## status: 1 |
| 264 | ## stdout-json: "" |
| 265 | ## N-I bash status: 0 |
| 266 | ## N-I bash STDOUT: |
| 267 | ale |
| 268 | ## END |
| 269 | |
| 270 | #### var ref TO array var |
| 271 | shopt -s eval_unsafe_arith compat_array |
| 272 | |
| 273 | declare -a array=(ale bean) |
| 274 | |
| 275 | ref='array' # when compat_array is on, this is like array[0] |
| 276 | ref_AT='array[@]' |
| 277 | |
| 278 | echo ${!ref} |
| 279 | echo ${!ref_AT} |
| 280 | |
| 281 | ## STDOUT: |
| 282 | ale |
| 283 | ale bean |
| 284 | ## END |
| 285 | |
| 286 | #### var ref TO array var, with subscripts |
| 287 | shopt -s eval_unsafe_arith |
| 288 | |
| 289 | f() { |
| 290 | argv.py "${!1}" |
| 291 | } |
| 292 | f 'nonexistent[0]' |
| 293 | array=(x y z) |
| 294 | f 'array[0]' |
| 295 | f 'array[1+1]' |
| 296 | f 'array[@]' |
| 297 | f 'array[*]' |
| 298 | # Also associative arrays. |
| 299 | ## STDOUT: |
| 300 | [''] |
| 301 | ['x'] |
| 302 | ['z'] |
| 303 | ['x', 'y', 'z'] |
| 304 | ['x y z'] |
| 305 | ## END |
| 306 | |
| 307 | #### var ref TO assoc array a[key] |
| 308 | shopt -s eval_unsafe_arith compat_array |
| 309 | |
| 310 | declare -A assoc=([ale]=bean [corn]=dip) |
| 311 | ref=assoc |
| 312 | #ref_AT='assoc[@]' |
| 313 | |
| 314 | # UNQUOTED doesn't work with Oil's parser |
| 315 | #ref_SUB='assoc[ale]' |
| 316 | ref_SUB='assoc["ale"]' |
| 317 | |
| 318 | ref_SUB_QUOTED='assoc["al"e]' |
| 319 | |
| 320 | ref_SUB_BAD='assoc["bad"]' |
| 321 | |
| 322 | echo ref=${!ref} # compat_array: assoc is equivalent to assoc[0] |
| 323 | #echo ref_AT=${!ref_AT} |
| 324 | echo ref_SUB=${!ref_SUB} |
| 325 | echo ref_SUB_QUOTED=${!ref_SUB_QUOTED} |
| 326 | echo ref_SUB_BAD=${!ref_SUB_BAD} |
| 327 | |
| 328 | ## STDOUT: |
| 329 | ref= |
| 330 | ref_SUB=bean |
| 331 | ref_SUB_QUOTED=bean |
| 332 | ref_SUB_BAD= |
| 333 | ## END |
| 334 | |
| 335 | #### var ref TO array with arbitrary subscripts |
| 336 | shopt -s eval_unsafe_arith compat_array |
| 337 | |
| 338 | f() { |
| 339 | local val=$(echo "${!1}") |
| 340 | if test "$val" = y; then |
| 341 | echo "works: $1" |
| 342 | fi |
| 343 | } |
| 344 | # Warmup: nice plain array reference |
| 345 | a=(x y) |
| 346 | f 'a[1]' |
| 347 | # |
| 348 | # Not allowed: |
| 349 | # no brace expansion |
| 350 | f 'a[{1,0}]' # operand expected |
| 351 | # no process substitution (but see command substitution below!) |
| 352 | f 'a[<(echo x)]' # operand expected |
| 353 | # TODO word splitting seems interesting |
| 354 | aa="1 0" |
| 355 | f 'a[$aa]' # 1 0: syntax error in expression (error token is "0") |
| 356 | # no filename globbing |
| 357 | f 'a[b*]' # operand expected |
| 358 | f 'a[1"]' # bad substitution |
| 359 | # |
| 360 | # Allowed: most everything else in section 3.5 "Shell Expansions". |
| 361 | # shell parameter expansion |
| 362 | b=1 |
| 363 | f 'a[$b]' |
| 364 | f 'a[${c:-1}]' |
| 365 | # (... and presumably most of the other features there) |
| 366 | # command substitution, yikes! |
| 367 | f 'a[$(echo 1)]' |
| 368 | # arithmetic expansion |
| 369 | f 'a[$(( 3 - 2 ))]' |
| 370 | |
| 371 | # All of these are undocumented and probably shouldn't exist, |
| 372 | # though it's always possible some will turn up in the wild and |
| 373 | # we'll end up implementing them. |
| 374 | |
| 375 | ## STDOUT: |
| 376 | works: a[1] |
| 377 | works: a[$b] |
| 378 | works: a[${c:-1}] |
| 379 | works: a[$(echo 1)] |
| 380 | works: a[$(( 3 - 2 ))] |
| 381 | ## END |
| 382 | |
| 383 | #### Bizarre tilde expansion in array index |
| 384 | a=(x y) |
| 385 | PWD=1 |
| 386 | ref='a[~+]' |
| 387 | echo ${!ref} |
| 388 | ## status: 1 |
| 389 | ## BUG bash status: 0 |
| 390 | ## BUG bash STDOUT: |
| 391 | y |
| 392 | ## END |
| 393 | |
| 394 | #### Indirect expansion TO fancy expansion features bash disallows |
| 395 | shopt -s eval_unsafe_arith |
| 396 | |
| 397 | check_indir() { |
| 398 | result="${!1}" |
| 399 | desugared_result=$(eval 'echo "${'"$1"'}"') |
| 400 | [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; } |
| 401 | } |
| 402 | x=y |
| 403 | y=a |
| 404 | a=(x y) |
| 405 | declare -A aa |
| 406 | aa=([k]=r [l]=s) |
| 407 | # malformed array indexing |
| 408 | check_indir "a[0" |
| 409 | check_indir "aa[k" |
| 410 | # double indirection |
| 411 | check_indir "!x" a |
| 412 | check_indir "!a[0]" y |
| 413 | # apparently everything else in the manual under "Shell Parameter Expansion" |
| 414 | check_indir "x:-foo" y |
| 415 | check_indir "x:=foo" y |
| 416 | check_indir "x:?oops" y |
| 417 | check_indir "x:+yy" yy |
| 418 | check_indir "x:0" y |
| 419 | check_indir "x:0:1" y |
| 420 | check_indir "!a@" "a aa" |
| 421 | # (!a[@] is elsewhere) |
| 422 | check_indir "#x" 1 |
| 423 | check_indir "x#y" |
| 424 | check_indir "x/y/foo" foo |
| 425 | check_indir "x@Q" "'y'" |
| 426 | echo done |
| 427 | ## status: 1 |
| 428 | ## stdout-json: "" |
| 429 | ## OK bash status: 0 |
| 430 | ## OK bash stdout: done |
| 431 | |
| 432 | #### Bad var ref |
| 433 | a='bad var name' |
| 434 | echo ref ${!a} |
| 435 | echo status=$? |
| 436 | |
| 437 | ## STDOUT: |
| 438 | status=1 |
| 439 | ## END |
| 440 | ## OK osh stdout-json: "" |
| 441 | ## OK osh status: 1 |
| 442 | |
| 443 | #### Bad var ref 2 |
| 444 | b='/' # really bad |
| 445 | echo ref ${!b} |
| 446 | echo status=$? |
| 447 | ## STDOUT: |
| 448 | status=1 |
| 449 | ## END |
| 450 | ## OK osh stdout-json: "" |
| 451 | ## OK osh status: 1 |
| 452 | |
| 453 | #### ${!OPTIND} (used by bash completion |
| 454 | set -- a b c |
| 455 | echo ${!OPTIND} |
| 456 | f() { |
| 457 | local OPTIND=1 |
| 458 | echo ${!OPTIND} |
| 459 | local OPTIND=2 |
| 460 | echo ${!OPTIND} |
| 461 | } |
| 462 | f x y z |
| 463 | ## STDOUT: |
| 464 | a |
| 465 | x |
| 466 | y |
| 467 | ## END |
| 468 | |
| 469 | #### var ref doesn't need cycle detection |
| 470 | x=y |
| 471 | y=x |
| 472 | echo cycle=${!x} |
| 473 | |
| 474 | typeset -n a=b |
| 475 | typeset -n b=a |
| 476 | echo cycle=${a} |
| 477 | ## status: 1 |
| 478 | ## STDOUT: |
| 479 | cycle=x |
| 480 | ## END |
| 481 | ## OK bash status: 0 |
| 482 | ## OK bash STDOUT: |
| 483 | cycle=x |
| 484 | cycle= |
| 485 | ## END |