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