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