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 |