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 |
245 |
shopt -s compat_array |
246 |
|
247 |
declare -a array=(ale bean) |
248 |
ref='array[0]' |
249 |
echo ${!ref} |
250 |
## status: 0 |
251 |
## STDOUT: |
252 |
ale |
253 |
## END |
254 |
|
255 |
#### array ref without compat_array |
256 |
|
257 |
declare -a array=(ale bean) |
258 |
ref='array' |
259 |
echo ${!ref} |
260 |
## status: 1 |
261 |
## stdout-json: "" |
262 |
## N-I bash status: 0 |
263 |
## N-I bash STDOUT: |
264 |
ale |
265 |
## END |
266 |
|
267 |
#### var ref TO array var |
268 |
shopt -s compat_array |
269 |
|
270 |
declare -a array=(ale bean) |
271 |
|
272 |
ref='array' # when compat_array is on, this is like array[0] |
273 |
ref_AT='array[@]' |
274 |
|
275 |
echo ${!ref} |
276 |
echo ${!ref_AT} |
277 |
|
278 |
## STDOUT: |
279 |
ale |
280 |
ale bean |
281 |
## END |
282 |
|
283 |
#### var ref TO array var, with subscripts |
284 |
f() { |
285 |
argv.py "${!1}" |
286 |
} |
287 |
f 'nonexistent[0]' |
288 |
array=(x y z) |
289 |
f 'array[0]' |
290 |
f 'array[1+1]' |
291 |
f 'array[@]' |
292 |
f 'array[*]' |
293 |
# Also associative arrays. |
294 |
## STDOUT: |
295 |
[''] |
296 |
['x'] |
297 |
['z'] |
298 |
['x', 'y', 'z'] |
299 |
['x y z'] |
300 |
## END |
301 |
|
302 |
#### var ref TO assoc array a[key] |
303 |
shopt -s compat_array |
304 |
|
305 |
declare -A assoc=([ale]=bean [corn]=dip) |
306 |
ref=assoc |
307 |
#ref_AT='assoc[@]' |
308 |
|
309 |
# UNQUOTED doesn't work with Oil's parser |
310 |
#ref_SUB='assoc[ale]' |
311 |
ref_SUB='assoc["ale"]' |
312 |
|
313 |
ref_SUB_QUOTED='assoc["al"e]' |
314 |
|
315 |
ref_SUB_BAD='assoc["bad"]' |
316 |
|
317 |
echo ref=${!ref} # compat_array: assoc is equivalent to assoc[0] |
318 |
#echo ref_AT=${!ref_AT} |
319 |
echo ref_SUB=${!ref_SUB} |
320 |
echo ref_SUB_QUOTED=${!ref_SUB_QUOTED} |
321 |
echo ref_SUB_BAD=${!ref_SUB_BAD} |
322 |
|
323 |
## STDOUT: |
324 |
ref= |
325 |
ref_SUB=bean |
326 |
ref_SUB_QUOTED=bean |
327 |
ref_SUB_BAD= |
328 |
## END |
329 |
|
330 |
#### var ref TO array with arbitrary subscripts |
331 |
shopt -s eval_unsafe_arith compat_array |
332 |
|
333 |
f() { |
334 |
local val=$(echo "${!1}") |
335 |
if test "$val" = y; then |
336 |
echo "works: $1" |
337 |
fi |
338 |
} |
339 |
# Warmup: nice plain array reference |
340 |
a=(x y) |
341 |
f 'a[1]' |
342 |
# |
343 |
# Not allowed: |
344 |
# no brace expansion |
345 |
f 'a[{1,0}]' # operand expected |
346 |
# no process substitution (but see command substitution below!) |
347 |
f 'a[<(echo x)]' # operand expected |
348 |
# TODO word splitting seems interesting |
349 |
aa="1 0" |
350 |
f 'a[$aa]' # 1 0: syntax error in expression (error token is "0") |
351 |
# no filename globbing |
352 |
f 'a[b*]' # operand expected |
353 |
f 'a[1"]' # bad substitution |
354 |
# |
355 |
# Allowed: most everything else in section 3.5 "Shell Expansions". |
356 |
# shell parameter expansion |
357 |
b=1 |
358 |
f 'a[$b]' |
359 |
f 'a[${c:-1}]' |
360 |
# (... and presumably most of the other features there) |
361 |
# command substitution, yikes! |
362 |
f 'a[$(echo 1)]' |
363 |
# arithmetic expansion |
364 |
f 'a[$(( 3 - 2 ))]' |
365 |
|
366 |
# All of these are undocumented and probably shouldn't exist, |
367 |
# though it's always possible some will turn up in the wild and |
368 |
# we'll end up implementing them. |
369 |
|
370 |
## STDOUT: |
371 |
works: a[1] |
372 |
works: a[$b] |
373 |
works: a[${c:-1}] |
374 |
works: a[$(echo 1)] |
375 |
works: a[$(( 3 - 2 ))] |
376 |
## END |
377 |
|
378 |
#### Bizarre tilde expansion in array index |
379 |
a=(x y) |
380 |
PWD=1 |
381 |
ref='a[~+]' |
382 |
echo ${!ref} |
383 |
## status: 1 |
384 |
## BUG bash status: 0 |
385 |
## BUG bash STDOUT: |
386 |
y |
387 |
## END |
388 |
|
389 |
#### Indirect expansion TO fancy expansion features bash disallows |
390 |
|
391 |
check_indir() { |
392 |
result="${!1}" |
393 |
desugared_result=$(eval 'echo "${'"$1"'}"') |
394 |
[ "$2" = "$desugared_result" ] || { echo "$1 $desugared_result"; } |
395 |
} |
396 |
x=y |
397 |
y=a |
398 |
a=(x y) |
399 |
declare -A aa |
400 |
aa=([k]=r [l]=s) |
401 |
# malformed array indexing |
402 |
check_indir "a[0" |
403 |
check_indir "aa[k" |
404 |
# double indirection |
405 |
check_indir "!x" a |
406 |
check_indir "!a[0]" y |
407 |
# apparently everything else in the manual under "Shell Parameter Expansion" |
408 |
check_indir "x:-foo" y |
409 |
check_indir "x:=foo" y |
410 |
check_indir "x:?oops" y |
411 |
check_indir "x:+yy" yy |
412 |
check_indir "x:0" y |
413 |
check_indir "x:0:1" y |
414 |
check_indir "!a@" "a aa" |
415 |
# (!a[@] is elsewhere) |
416 |
check_indir "#x" 1 |
417 |
check_indir "x#y" |
418 |
check_indir "x/y/foo" foo |
419 |
check_indir "x@Q" "'y'" |
420 |
echo done |
421 |
## status: 1 |
422 |
## stdout-json: "" |
423 |
## OK bash status: 0 |
424 |
## OK bash stdout: done |
425 |
|
426 |
#### Bad var ref |
427 |
a='bad var name' |
428 |
echo ref ${!a} |
429 |
echo status=$? |
430 |
|
431 |
## STDOUT: |
432 |
status=1 |
433 |
## END |
434 |
## OK osh stdout-json: "" |
435 |
## OK osh status: 1 |
436 |
|
437 |
#### Bad var ref 2 |
438 |
b='/' # really bad |
439 |
echo ref ${!b} |
440 |
echo status=$? |
441 |
## STDOUT: |
442 |
status=1 |
443 |
## END |
444 |
## OK osh stdout-json: "" |
445 |
## OK osh status: 1 |
446 |
|
447 |
#### ${!OPTIND} (used by bash completion |
448 |
set -- a b c |
449 |
echo ${!OPTIND} |
450 |
f() { |
451 |
local OPTIND=1 |
452 |
echo ${!OPTIND} |
453 |
local OPTIND=2 |
454 |
echo ${!OPTIND} |
455 |
} |
456 |
f x y z |
457 |
## STDOUT: |
458 |
a |
459 |
x |
460 |
y |
461 |
## END |
462 |
|
463 |
#### var ref doesn't need cycle detection |
464 |
x=y |
465 |
y=x |
466 |
echo cycle=${!x} |
467 |
|
468 |
typeset -n a=b |
469 |
typeset -n b=a |
470 |
echo cycle=${a} |
471 |
## status: 1 |
472 |
## STDOUT: |
473 |
cycle=x |
474 |
## END |
475 |
## OK bash status: 0 |
476 |
## OK bash STDOUT: |
477 |
cycle=x |
478 |
cycle= |
479 |
## END |
480 |
|
481 |
#### Var Ref Code Injection $(tee PWNED) |
482 |
|
483 |
typeset -a a |
484 |
a=(42) |
485 |
|
486 |
x='a[$(echo 0 | tee PWNED)]' |
487 |
|
488 |
echo ${!x} |
489 |
|
490 |
if test -f PWNED; then |
491 |
echo PWNED |
492 |
cat PWNED |
493 |
else |
494 |
echo NOPE |
495 |
fi |
496 |
|
497 |
## status: 1 |
498 |
## STDOUT: |
499 |
## END |
500 |
|
501 |
## BUG bash status: 0 |
502 |
## BUG bash STDOUT: |
503 |
42 |
504 |
PWNED |
505 |
0 |
506 |
## END |