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