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 #### ref to $@ with @
102 set -- one two
103 ref='@'
104 echo ref=${!ref}
105 ## STDOUT:
106 ref=one two
107 ## END
108
109 #### 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 with 1 and @ and *
122 set -- x y
123 ref=1; printf "|%s" "${!ref}" $'\n'
124 ref=@; printf "|%s" "${!ref}" $'\n'
125 ref=*; printf "|%s" "${!ref}" $'\n'
126 ## STDOUT:
127 |x|
128 |x|y|
129 |x y|
130 ## END
131
132 #### var ref to special var BASH_SOURCE
133 ref='LINENO'
134 echo lineno=${!ref}
135 ## STDOUT:
136 lineno=2
137 ## END
138
139 #### var ref to $? with '?' (not in Oil)
140 myfunc() {
141 local ref=$1
142 echo ${!ref}
143 }
144 myfunc FUNCNAME
145 myfunc '?' # osh doesn't do this dynamically
146 ## STDOUT:
147 myfunc
148 0
149 ## END
150 ## N-I osh STDOUT:
151 myfunc
152 ## END
153 ## N-I osh status: 1
154
155 #### indirection, *then* fancy expansion features
156 check_eq() {
157 [ "$1" = "$2" ] || { echo "$1 vs $2"; }
158 }
159 check_expand() {
160 val=$(eval "echo \"$1\"")
161 [ "$val" = "$2" ] || { echo "$1 -> expected $2, got $val"; }
162 }
163 check_err() {
164 e="$1"
165 msg=$(eval "$e" 2>&1) && echo "bad success: $e"
166 [ -z "$2" ] || [[ "$msg" == $2 ]] || echo "bad err msg: $e -> $msg"
167 }
168 # Nearly everything in manual section 3.5.3 "Shell Parameter Expansion"
169 # is allowed after a !-indirection.
170 #
171 # Not allowed: any further prefix syntax.
172 x=xx; xx=aaabcc
173 xd=x
174 check_err '${!!xd}'
175 check_err '${!!x*}'
176 a=(asdf x)
177 check_err '${!!a[*]}'
178 check_err '${!#x}'
179 check_err '${!#a[@]}'
180 # And an array reference binds tighter in the syntax, so goes first;
181 # there's no way to spell "indirection, then array reference".
182 check_expand '${!a[1]}' xx
183 b=(aoeu a)
184 check_expand '${!b[1]}' asdf # i.e. like !(b[1]), not (!b)[1]
185 #
186 # Allowed: apparently everything else.
187 y=yy; yy=
188 check_expand '${!y:-foo}' foo
189 check_expand '${!x:-foo}' aaabcc
190 z=zz; zz=
191 check_eq "${!z:=foo}" foo ; check_expand '$zz' foo
192 check_eq "${!z:=bar}" foo ; check_expand '$zz' foo
193 w=ww; ww=
194 check_err '${!w:?oops}' '*: oops'
195 check_expand '${!x:?oops}' aaabcc
196 check_expand '${!y:+foo}' ''
197 check_expand '${!x:+foo}' foo
198 check_expand '${!x:2}' abcc
199 check_expand '${!x:2:2}' ab
200 check_expand '${!x#*a}' aabcc
201 check_expand '${!x%%c*}' aaab
202 check_expand '${!x/a*b/d}' dcc
203 check_expand '${!x^a}' Aaabcc
204 p=pp; pp='\$ '
205 check_expand '${!p@P}' '$ '
206 echo ok
207 ## stdout: ok
208
209 #### indirection *to* an array reference
210 f() {
211 printf ".%s" "${!1}"
212 echo
213 }
214 f a[0]
215 b=(x y)
216 f b[0]
217 f b[@]
218 f "b[*]"
219 # Also associative arrays.
220 ## STDOUT:
221 .
222 .x
223 .x.y
224 .x y
225 ## END
226
227 #### indirection to nasty complex array references
228 i=0
229 f() {
230 ((i++))
231 val=$(echo "${!1}")
232 [ "$val" = y ] && echo -n "$i "
233 }
234 # Warmup: nice plain array reference
235 a=(x y)
236 f 'a[1]'
237 #
238 # Not allowed:
239 # no brace expansion
240 f 'a[{1,0}]' # operand expected
241 # no process substitution (but see command substitution below!)
242 f 'a[<(echo x)]' # operand expected
243 # TODO word splitting seems interesting
244 aa="1 0"
245 f 'a[$aa]' # 1 0: syntax error in expression (error token is "0")
246 # no filename globbing
247 f 'a[b*]' # operand expected
248 f 'a[1"]' # bad substitution
249 #
250 # Allowed: most everything else in section 3.5 "Shell Expansions".
251 # tilde expansion
252 ( PWD=1; f 'a[~+]' ); ((i++))
253 # shell parameter expansion
254 b=1
255 f 'a[$b]'
256 f 'a[${c:-1}]'
257 # (... and presumably most of the other features there)
258 # command substitution, yikes!
259 f 'a[$(echo 1)]'
260 # arithmetic expansion
261 f 'a[$(( 3 - 2 ))]'
262 echo end
263 # All of these are undocumented and probably shouldn't exist,
264 # though it's always possible some will turn up in the wild and
265 # we'll end up implementing them.
266 ## stdout: 1 end
267 ## OK bash stdout: 1 7 8 9 10 11 end
268
269 #### indirection *to* fancy expansion features bash disallows
270 check_indir() {
271 result="${!1}"
272 desugared_result=$(eval 'echo "${'"$1"'}"')
273 [ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; }
274 }
275 x=y
276 y=a
277 a=(x y)
278 declare -A aa
279 aa=([k]=r [l]=s)
280 # malformed array indexing
281 check_indir "a[0"
282 check_indir "aa[k"
283 # double indirection
284 check_indir "!x" a
285 check_indir "!a[0]" y
286 # apparently everything else in the manual under "Shell Parameter Expansion"
287 check_indir "x:-foo" y
288 check_indir "x:=foo" y
289 check_indir "x:?oops" y
290 check_indir "x:+yy" yy
291 check_indir "x:0" y
292 check_indir "x:0:1" y
293 check_indir "!a@" "a aa"
294 # (!a[@] is elsewhere)
295 check_indir "#x" 1
296 check_indir "x#y"
297 check_indir "x/y/foo" foo
298 check_indir "x@Q" "'y'"
299 echo done
300 ## status: 1
301 ## stdout-json: ""
302 ## OK bash status: 0
303 ## OK bash stdout: done
304
305 #### Bad var ref with ${!a}
306 a='bad var name'
307 echo ref ${!a}
308 echo status=$?
309 ## STDOUT:
310 status=1
311 ## END
312
313 # this error is fatal in osh
314 ## OK osh stdout-json: ""
315 ## OK osh status: 1
316
317 #### ${!OPTIND} (used by bash completion
318 set -- a b c
319 echo ${!OPTIND}
320 f() {
321 local OPTIND=1
322 echo ${!OPTIND}
323 local OPTIND=2
324 echo ${!OPTIND}
325 }
326 f x y z
327 ## STDOUT:
328 a
329 x
330 y
331 ## END
332