| 1 | #!/usr/bin/env bash |
| 2 | # |
| 3 | # Var refs are done with ${!a} |
| 4 | # |
| 5 | # local/declare -n is tested in spec/named-ref.test.sh. |
| 6 | # |
| 7 | # http://stackoverflow.com/questions/16461656/bash-how-to-pass-array-as-an-argument-to-a-function |
| 8 | |
| 9 | #### var ref ${!a} |
| 10 | a=b |
| 11 | b=c |
| 12 | echo ref ${!a} ${a} |
| 13 | ## stdout: ref c b |
| 14 | |
| 15 | #### var ref: positional params |
| 16 | set -- x y |
| 17 | ref=1; printf "|%s" "${!ref}" $'\n' |
| 18 | ref=@; printf "|%s" "${!ref}" $'\n' |
| 19 | ref=*; printf "|%s" "${!ref}" $'\n' |
| 20 | ## STDOUT: |
| 21 | |x| |
| 22 | |x|y| |
| 23 | |x y| |
| 24 | ## END |
| 25 | |
| 26 | #### var ref with special vars |
| 27 | myfunc() { |
| 28 | local ref=$1 |
| 29 | echo ${!ref} |
| 30 | } |
| 31 | myfunc FUNCNAME |
| 32 | myfunc '?' # osh doesn't do this dynamically |
| 33 | ## STDOUT: |
| 34 | myfunc |
| 35 | 0 |
| 36 | ## END |
| 37 | |
| 38 | #### indirection, *then* fancy expansion features |
| 39 | check_eq() { |
| 40 | [ "$1" = "$2" ] || { echo "$1 vs $2"; } |
| 41 | } |
| 42 | check_expand() { |
| 43 | val=$(eval "echo \"$1\"") |
| 44 | [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; } |
| 45 | } |
| 46 | check_err() { |
| 47 | e="$1" |
| 48 | msg=$(eval "$e" 2>&1) && echo "bad success: $e" |
| 49 | [ -z "$2" ] || [[ "$msg" == $2 ]] || echo "bad err msg: $e -> $msg" |
| 50 | } |
| 51 | # Nearly everything in manual section 3.5.3 "Shell Parameter Expansion" |
| 52 | # is allowed after a !-indirection. |
| 53 | # |
| 54 | # Not allowed: any further prefix syntax. |
| 55 | x=xx; xx=aaabcc |
| 56 | xd=x |
| 57 | check_err '${!!xd}' |
| 58 | check_err '${!!x*}' |
| 59 | a=(asdf x) |
| 60 | check_err '${!!a[*]}' |
| 61 | check_err '${!#x}' |
| 62 | check_err '${!#a[@]}' |
| 63 | # And an array reference binds tighter in the syntax, so goes first; |
| 64 | # there's no way to spell "indirection, then array reference". |
| 65 | check_expand '${!a[1]}' xx |
| 66 | b=(aoeu a) |
| 67 | check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1] |
| 68 | # |
| 69 | # Allowed: apparently everything else. |
| 70 | y=yy; yy= |
| 71 | check_expand '${!y:-foo}' foo |
| 72 | check_expand '${!x:-foo}' aaabcc |
| 73 | z=zz; zz= |
| 74 | check_eq "${!z:=foo}" foo ; check_expand '$zz' foo |
| 75 | check_eq "${!z:=bar}" foo ; check_expand '$zz' foo |
| 76 | w=ww; ww= |
| 77 | check_err '${!w:?oops}' '*: oops' |
| 78 | check_expand '${!x:?oops}' aaabcc |
| 79 | check_expand '${!y:+foo}' '' |
| 80 | check_expand '${!x:+foo}' foo |
| 81 | check_expand '${!x:2}' abcc |
| 82 | check_expand '${!x:2:2}' ab |
| 83 | check_expand '${!x#*a}' aabcc |
| 84 | check_expand '${!x%%c*}' aaab |
| 85 | check_expand '${!x/a*b/d}' dcc |
| 86 | check_expand '${!x^a}' Aaabcc |
| 87 | p=pp; pp='\$ ' |
| 88 | check_expand '${!p@P}' '$ ' |
| 89 | echo ok |
| 90 | ## stdout: ok |
| 91 | |
| 92 | #### indirection *to* an array reference |
| 93 | f() { |
| 94 | printf ".%s" "${!1}" |
| 95 | echo |
| 96 | } |
| 97 | f a[0] |
| 98 | b=(x y) |
| 99 | f b[0] |
| 100 | f b[@] |
| 101 | f "b[*]" |
| 102 | # Also associative arrays. |
| 103 | ## STDOUT: |
| 104 | . |
| 105 | .x |
| 106 | .x.y |
| 107 | .x y |
| 108 | ## END |
| 109 | |
| 110 | #### indirection to nasty complex array references |
| 111 | i=0 |
| 112 | f() { |
| 113 | ((i++)) |
| 114 | val=$(echo "${!1}") |
| 115 | [ "$val" = y ] && echo -n "$i " |
| 116 | } |
| 117 | # Warmup: nice plain array reference |
| 118 | a=(x y) |
| 119 | f 'a[1]' |
| 120 | # |
| 121 | # Not allowed: |
| 122 | # no brace expansion |
| 123 | f 'a[{1,0}]' # operand expected |
| 124 | # no process substitution (but see command substitution below!) |
| 125 | f 'a[<(echo x)]' # operand expected |
| 126 | # TODO word splitting seems interesting |
| 127 | aa="1 0" |
| 128 | f 'a[$aa]' # 1 0: syntax error in expression (error token is "0") |
| 129 | # no filename globbing |
| 130 | f 'a[b*]' # operand expected |
| 131 | f 'a[1"]' # bad substitution |
| 132 | # |
| 133 | # Allowed: most everything else in section 3.5 "Shell Expansions". |
| 134 | # tilde expansion |
| 135 | ( PWD=1; f 'a[~+]' ); ((i++)) |
| 136 | # shell parameter expansion |
| 137 | b=1 |
| 138 | f 'a[$b]' |
| 139 | f 'a[${c:-1}]' |
| 140 | # (... and presumably most of the other features there) |
| 141 | # command substitution, yikes! |
| 142 | f 'a[$(echo 1)]' |
| 143 | # arithmetic expansion |
| 144 | f 'a[$(( 3 - 2 ))]' |
| 145 | echo end |
| 146 | # All of these are undocumented and probably shouldn't exist, |
| 147 | # though it's always possible some will turn up in the wild and |
| 148 | # we'll end up implementing them. |
| 149 | ## stdout: 1 end |
| 150 | ## OK bash stdout: 1 7 8 9 10 11 end |
| 151 | |
| 152 | #### indirection *to* fancy expansion features bash disallows |
| 153 | check_indir() { |
| 154 | result="${!1}" |
| 155 | desugared_result=$(eval 'echo "${'"$1"'}"') |
| 156 | [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; } |
| 157 | } |
| 158 | x=y |
| 159 | y=a |
| 160 | a=(x y) |
| 161 | declare -A aa |
| 162 | aa=([k]=r [l]=s) |
| 163 | # malformed array indexing |
| 164 | check_indir "a[0" |
| 165 | check_indir "aa[k" |
| 166 | # double indirection |
| 167 | check_indir "!x" a |
| 168 | check_indir "!a[0]" y |
| 169 | # apparently everything else in the manual under "Shell Parameter Expansion" |
| 170 | check_indir "x:-foo" y |
| 171 | check_indir "x:=foo" y |
| 172 | check_indir "x:?oops" y |
| 173 | check_indir "x:+yy" yy |
| 174 | check_indir "x:0" y |
| 175 | check_indir "x:0:1" y |
| 176 | check_indir "!a@" "a aa" |
| 177 | # (!a[@] is elsewhere) |
| 178 | check_indir "#x" 1 |
| 179 | check_indir "x#y" |
| 180 | check_indir "x/y/foo" foo |
| 181 | check_indir "x@Q" "'y'" |
| 182 | echo done |
| 183 | ## status: 1 |
| 184 | ## stdout-json: "" |
| 185 | ## OK bash status: 0 |
| 186 | ## OK bash stdout: done |
| 187 | |
| 188 | #### declare -n and ${!a} |
| 189 | declare -n a |
| 190 | a=b |
| 191 | b=c |
| 192 | echo ${!a} ${a} |
| 193 | ## stdout: b c |
| 194 | |
| 195 | #### Bad var ref with ${!a} |
| 196 | #set -o nounset |
| 197 | a='bad var name' |
| 198 | echo ref ${!a} |
| 199 | echo status=$? |
| 200 | ## STDOUT: |
| 201 | status=1 |
| 202 | ## END |
| 203 | |
| 204 | #### ${!OPTIND} (used by bash completion |
| 205 | set -- a b c |
| 206 | echo ${!OPTIND} |
| 207 | f() { |
| 208 | local OPTIND=1 |
| 209 | echo ${!OPTIND} |
| 210 | local OPTIND=2 |
| 211 | echo ${!OPTIND} |
| 212 | } |
| 213 | f x y z |
| 214 | ## STDOUT: |
| 215 | a |
| 216 | x |
| 217 | y |
| 218 | ## END |