1 #!/usr/bin/env bash
2
3 # TODO: Need a SETUP section.
4
5 ### SETUP
6 a=(1 '2 3')
7
8 ### "${a[@]}" and "${a[*]}"
9 a=(1 '2 3')
10 argv.py "${a[@]}" "${a[*]}"
11 # stdout: ['1', '2 3', '1 2 3']
12
13 ### ${a[@]} and ${a[*]}
14 a=(1 '2 3')
15 argv.py ${a[@]} ${a[*]}
16 # stdout: ['1', '2', '3', '1', '2', '3']
17
18 ### Empty array tests
19 argv.py 1 "${a[@]}" 2 ${a[@]} 3 "${a[*]}" 4 ${a[*]} 5
20 # stdout: ['1', '2', '3', '', '4', '5']
21
22 ### local array
23 # mksh support local variables, but not local arrays, oddly.
24 f() {
25 local a=(1 '2 3')
26 argv.py "${a[0]}"
27 }
28 f
29 # stdout: ['1']
30 # status: 0
31 # BUG mksh status: 1
32 # BUG mksh stdout-json: ""
33
34 ### Command with with word splitting in array
35 array=('1 2' $(echo '3 4'))
36 argv.py "${array[@]}"
37 # stdout: ['1 2', '3', '4']
38
39 ### space before ( in array initialization
40 # NOTE: mksh accepts this, but bash doesn't
41 a= (1 '2 3')
42 echo $a
43 # status: 2
44 # OK mksh status: 0
45 # OK mksh stdout: 1
46
47 ### array over multiple lines
48 a=(
49 1
50 '2 3'
51 )
52 argv.py "${a[@]}"
53 # stdout: ['1', '2 3']
54 # status: 0
55
56 ### array with invalid token
57 a=(
58 1
59 &
60 '2 3'
61 )
62 argv.py "${a[@]}"
63 # status: 2
64 # OK mksh status: 1
65
66 ### empty array
67 empty=()
68 argv.py "${empty[@]}"
69 # stdout: []
70
71 ### array with empty string
72 empty=('')
73 argv.py "${empty[@]}"
74 # stdout: ['']
75
76 ### Retrieve index
77 a=(1 '2 3')
78 argv.py "${a[1]}"
79 # stdout: ['2 3']
80
81 ### Retrieve out of bounds index
82 a=(1 '2 3')
83 argv.py "${a[3]}"
84 # stdout: ['']
85
86 ### Negative index
87 a=(1 '2 3')
88 argv.py "${a[-1]}" "${a[-2]}" "${a[-5]}" # last one out of bounds
89 # stdout: ['2 3', '1', '']
90 # N-I mksh stdout: ['', '', '']
91
92 ### Retrieve index that is a variable
93 a=(1 '2 3')
94 i=1
95 argv.py "${a[$i]}"
96 # stdout: ['2 3']
97
98 ### Retrieve index that is a variable without $
99 a=(1 '2 3')
100 i=5
101 argv.py "${a[i-4]}"
102 # stdout: ['2 3']
103
104 ### Retrieve index that is a command sub
105 a=(1 '2 3')
106 argv.py "${a[$(echo 1)]}"
107 # stdout: ['2 3']
108
109 ### Retrieve all indices with !
110 a=(1 '2 3')
111 argv.py "${!a[@]}"
112 # stdout: ['0', '1']
113
114 ### ${!a[1]} is named ref in bash
115 # mksh ignores it
116 foo=bar
117 a=('1 2' foo '2 3')
118 argv.py "${!a[1]}"
119 # status: 0
120 # stdout: ['bar']
121 # N-I mksh stdout: ['a[1]']
122
123 ### Retrieve indices without []
124 # bash gives empty string?
125 # mksh gives the name of the variable with !. Very weird.
126 a=(1 '2 3')
127 argv.py "${!a}"
128 # stdout: ['']
129 # OK mksh stdout: ['a']
130
131 ### All elements unquoted
132 a=(1 '2 3')
133 argv.py ${a[@]}
134 # stdout: ['1', '2', '3']
135
136 ### All elements quoted
137 a=(1 '2 3')
138 argv.py "${a[@]}"
139 # stdout: ['1', '2 3']
140
141 ### $*
142 a=(1 '2 3')
143 argv.py ${a[*]}
144 # stdout: ['1', '2', '3']
145
146 ### "$*"
147 a=(1 '2 3')
148 argv.py "${a[*]}"
149 # stdout: ['1 2 3']
150
151 ### Interpolate array into array
152 a=(1 '2 3')
153 a=(0 "${a[@]}" '4 5')
154 argv.py "${a[@]}"
155 # stdout: ['0', '1', '2 3', '4 5']
156
157 ### Exporting array doesn't do anything, not even first element
158 # bash parses, but doesn't execute.
159 # mksh gives syntax error -- parses differently with 'export'
160 # osh no longer parses this statically.
161 export PYTHONPATH=(a b c)
162 export PYTHONPATH=a # NOTE: in bash, this doesn't work afterward!
163 printenv.py PYTHONPATH
164 # stdout: None
165 # OK mksh stdout-json: ""
166 # OK mksh status: 1
167 # OK osh stdout-json: ""
168 # OK osh status: 2
169
170 ### Env with array
171 # Hm it treats it as a string!
172 A=a B=(b b) printenv.py A B
173 # stdout-json: "a\n(b b)\n"
174 # BUG mksh stdout-json: ""
175 # BUG mksh status: 1
176
177 ### Set element
178 a=(1 '2 3')
179 a[0]=9
180 argv.py "${a[@]}"
181 # stdout: ['9', '2 3']
182
183 ### Set element with var ref
184 a=(1 '2 3')
185 i=0
186 a[$i]=9
187 argv.py "${a[@]}"
188 # stdout: ['9', '2 3']
189
190 ### Set element with array ref
191 # This makes parsing a little more complex. Anything can be inside [],
192 # including other [].
193 a=(1 '2 3')
194 i=(0 1)
195 a[${i[1]}]=9
196 argv.py "${a[@]}"
197 # stdout: ['1', '9']
198
199 ### Set array item to array
200 a=(1 2)
201 a[0]=(3 4)
202 echo "status=$?"
203 # stdout: status=1
204 # status: 0
205 # N-I mksh stdout-json: ""
206 # N-I mksh status: 1
207
208 ### Slice of array with [@]
209 # mksh doesn't support this syntax! It's a bash extension.
210 a=(1 2 3)
211 argv.py "${a[@]:1:2}"
212 # stdout: ['2', '3']
213 # N-I mksh status: 1
214 # N-I mksh stdout-json: ""
215
216 ### Negative slice
217 # mksh doesn't support this syntax! It's a bash extension.
218 # NOTE: for some reason -2) has to be in parens? Ah that's because it
219 # conflicts with :-! That's silly. You can also add a space.
220 a=(1 2 3)
221 argv.py "${a[@]:(-2):1}"
222 # stdout: ['2']
223 # N-I mksh status: 1
224 # N-I mksh stdout-json: ""
225
226 ### Slice with arithmetic
227 a=(1 2 3)
228 i=5
229 argv.py "${a[@]:i-4:2}"
230 # stdout: ['2', '3']
231 # N-I mksh status: 1
232 # N-I mksh stdout-json: ""
233
234 ### Number of elements
235 a=(1 '2 3')
236 echo "${#a[@]}"
237 # stdout: 2
238
239 ### Length of an element
240 a=(1 '2 3')
241 echo "${#a[1]}"
242 # stdout: 3
243
244 ### Iteration
245 a=(1 '2 3')
246 for v in "${a[@]}"; do
247 echo $v
248 done
249 # stdout-json: "1\n2 3\n"
250
251 ### glob within array yields separate elements
252 touch _tmp/y.Y _tmp/yy.Y
253 a=(_tmp/*.Y)
254 argv.py "${a[@]}"
255 # stdout: ['_tmp/y.Y', '_tmp/yy.Y']
256
257 ### declare array and then append
258 declare -a array
259 array+=(a)
260 array+=(b c)
261 argv.py "${array[@]}"
262 # stdout: ['a', 'b', 'c']
263
264 ### Array syntax in wrong place
265 ls foo=(1 2)
266 # status: 2
267 # OK mksh status: 1
268
269 ### Empty array with :-
270 empty=()
271 argv.py ${empty[@]:-not one} "${empty[@]:-not one}"
272 # stdout: ['not', 'one', 'not one']
273
274 ### Single array with :-
275 # bash does EMPTY ELISION here, unless it's double quoted. mksh has
276 # more sane behavior. OSH is better.
277 single=('')
278 argv.py ${single[@]:-none} x "${single[@]:-none}"
279 # OK osh stdout: ['x', '']
280 # OK bash stdout: ['none', 'x', '']
281 # OK mksh stdout: ['none', 'x', 'none']
282
283 ### Stripping a whole array unquoted
284 # Problem: it joins it first.
285 files=('foo.c' 'sp ace.h' 'bar.c')
286 argv.py ${files[@]%.c}
287 # status: 0
288 # stdout: ['foo', 'sp', 'ace.h', 'bar']
289 # N-I mksh status: 1
290 # N-I mksh stdout-json: ""
291
292 ### Stripping a whole array quoted
293 files=('foo.c' 'sp ace.h' 'bar.c')
294 argv.py "${files[@]%.c}"
295 # status: 0
296 # stdout: ['foo', 'sp ace.h', 'bar']
297 # N-I mksh status: 1
298 # N-I mksh stdout-json: ""
299
300 ### Multiple subscripts not allowed
301 a=('123' '456')
302 argv.py "${a[0]}" "${a[0][0]}"
303 # stdout-json: ""
304 # status: 2
305 # OK mksh status: 1
306 # bash is bad -- it IGNORES the bad subscript.
307 # BUG bash status: 0
308 # BUG bash stdout: ['123', '123']
309
310 ### Length op, index op, then transform op is not allowed
311 a=('123' '456')
312 echo "${#a[0]}" "${#a[0]/1/xxx}"
313 # stdout-json: ""
314 # status: 2
315 # OK mksh status: 1
316 # bash is bad -- it IGNORES the op at the end
317 # BUG bash status: 0
318 # BUG bash stdout: 3 3
319
320 ### Array subscript not allowed on string
321 s='abc'
322 echo ${s[@]}
323 # BUG bash/mksh status: 0
324 # BUG bash/mksh stdout: abc
325 # status: 1
326
327 ### Create a "user" array out of the argv array
328 set -- 'a b' 'c'
329 array1=('x y' 'z')
330 array2=("$@")
331 argv.py "${array1[@]}" "${array2[@]}"
332 # stdout: ['x y', 'z', 'a b', 'c']
333
334 ### Tilde expansion within array
335 HOME=/home/bob
336 a=(~/src ~/git)
337 echo "${a[@]}"
338 # stdout: /home/bob/src /home/bob/git
339
340 ### Brace Expansion within Array
341 a=(-{a,b} {c,d}-)
342 echo "${a[@]}"
343 # stdout: -a -b c- d-
344
345 ### array default
346 default=('1 2' '3')
347 argv.py "${undef[@]:-${default[@]}}"
348 # stdout: ['1 2', '3']
349