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