1 # Test combination of var ops.
2 #
3 # NOTE: There are also slice tests in {array,arith-context}.test.sh.
4
5 ## compare_shells: bash mksh zsh
6
7 #### String slice
8 foo=abcdefg
9 echo ${foo:1:3}
10 ## STDOUT:
11 bcd
12 ## END
13
14 #### Cannot take length of substring slice
15 # These are runtime errors, but we could make them parse time errors.
16 v=abcde
17 echo ${#v:1:3}
18 ## status: 1
19 ## OK osh status: 2
20 # zsh actually implements this!
21 ## OK zsh stdout: 3
22 ## OK zsh status: 0
23
24 #### Out of range string slice: begin
25 # out of range begin doesn't raise error in bash, but in mksh it skips the
26 # whole thing!
27 foo=abcdefg
28 echo _${foo:100:3}
29 echo $?
30 ## STDOUT:
31 _
32 0
33 ## END
34 ## BUG mksh STDOUT:
35
36 0
37 ## END
38
39 #### Out of range string slice: length
40 # OK in both bash and mksh
41 foo=abcdefg
42 echo _${foo:3:100}
43 echo $?
44 ## STDOUT:
45 _defg
46 0
47 ## END
48 ## BUG mksh STDOUT:
49 _defg
50 0
51 ## END
52
53 #### Negative start index
54 foo=abcdefg
55 echo ${foo: -4:3}
56 ## stdout: def
57
58 #### Negative start index respects unicode
59 foo=abcd-μ-
60 echo ${foo: -4:3}
61 ## stdout: d-μ
62 ## BUG mksh stdout: -μ
63
64 #### Negative second arg is position, not length!
65 foo=abcdefg
66 echo ${foo:3:-1} ${foo: 3: -2} ${foo:3 :-3 }
67 ## stdout: def de d
68 ## BUG mksh stdout: defg defg defg
69
70 #### Negative start index respects unicode
71 foo=abcd-μ-
72 echo ${foo: -5: -3}
73 ## stdout: cd
74 ## BUG mksh stdout: d-μ-
75
76 #### String slice with math
77 # I think this is the $(()) language inside?
78 i=1
79 foo=abcdefg
80 echo ${foo: i+4-2 : i + 2}
81 ## stdout: def
82
83 #### Slice undefined
84 echo -${undef:1:2}-
85 set -o nounset
86 echo -${undef:1:2}-
87 echo -done-
88 ## STDOUT:
89 --
90 ## END
91 ## status: 1
92 # mksh doesn't respect nounset!
93 ## BUG mksh status: 0
94 ## BUG mksh STDOUT:
95 --
96 --
97 -done-
98 ## END
99
100 #### Slice UTF-8 String
101 # mksh slices by bytes.
102 foo='--μ--'
103 echo ${foo:1:3}
104 ## stdout: -μ-
105 ## BUG mksh stdout: -μ
106
107 #### Slice string with invalid UTF-8 results in empty string and warning
108 s=$(echo -e "\xFF")bcdef
109 echo -${s:1:3}-
110 ## status: 0
111 ## STDOUT:
112 --
113 ## END
114 ## STDERR:
115 [??? no location ???] warning: UTF-8 decode: Bad encoding at offset 0 in string of 6 bytes
116 ## END
117 ## BUG bash/mksh/zsh status: 0
118 ## BUG bash/mksh/zsh STDOUT:
119 -bcd-
120 ## END
121 ## BUG bash/mksh/zsh stderr-json: ""
122
123
124 #### Slice string with invalid UTF-8 with strict_word_eval
125 shopt -s strict_word_eval || true
126 echo slice
127 s=$(echo -e "\xFF")bcdef
128 echo -${s:1:3}-
129 ## status: 1
130 ## STDOUT:
131 slice
132 ## END
133 ## N-I bash/mksh/zsh status: 0
134 ## N-I bash/mksh/zsh STDOUT:
135 slice
136 -bcd-
137 ## END
138
139 #### Slice with an index that's an array -- silent a[0] decay
140 i=(3 4 5)
141 mystr=abcdefg
142 echo assigned
143 echo ${mystr:$i:2}
144
145 ## status: 0
146 ## STDOUT:
147 assigned
148 de
149 ## END
150 ## OK zsh status: 1
151 ## OK zsh STDOUT:
152 assigned
153 ## END
154
155 #### Slice with an assoc array
156 declare -A A=(['5']=3 ['6']=4)
157 mystr=abcdefg
158 echo assigned
159 echo ${mystr:$A:2}
160
161 ## status: 0
162 ## STDOUT:
163 assigned
164 ab
165 ## END
166
167 ## N-I mksh status: 1
168 ## N-I mksh stdout-json: ""
169
170 #### Simple ${@:offset}
171
172 set -- 4 5 6
173
174 result=$(argv.py ${@:0})
175 echo ${result//"$0"/'SHELL'}
176
177 argv.py ${@:1}
178 argv.py ${@:2}
179
180 ## STDOUT:
181 ['SHELL', '4', '5', '6']
182 ['4', '5', '6']
183 ['5', '6']
184 ## END
185 ## N-I mksh status: 1
186 ## N-I mksh STDOUT:
187
188 ## END
189
190
191 #### ${@:offset} and ${*:offset}
192 case $SH in (zsh) return ;; esac # zsh is very different
193
194 argv.shell-name-checked () {
195 argv.py "${@//$0/SHELL}"
196 }
197 fun() {
198 argv.shell-name-checked -${*:0}- # include $0
199 argv.shell-name-checked -${*:1}- # from $1
200 argv.shell-name-checked -${*:3}- # last parameter $3
201 argv.shell-name-checked -${*:4}- # empty
202 argv.shell-name-checked -${*:5}- # out of boundary
203 argv.shell-name-checked -${@:0}-
204 argv.shell-name-checked -${@:1}-
205 argv.shell-name-checked -${@:3}-
206 argv.shell-name-checked -${@:4}-
207 argv.shell-name-checked -${@:5}-
208 argv.shell-name-checked "-${*:0}-"
209 argv.shell-name-checked "-${*:1}-"
210 argv.shell-name-checked "-${*:3}-"
211 argv.shell-name-checked "-${*:4}-"
212 argv.shell-name-checked "-${*:5}-"
213 argv.shell-name-checked "-${@:0}-"
214 argv.shell-name-checked "-${@:1}-"
215 argv.shell-name-checked "-${@:3}-"
216 argv.shell-name-checked "-${@:4}-"
217 argv.shell-name-checked "-${@:5}-"
218 }
219 fun "a 1" "b 2" "c 3"
220 ## STDOUT:
221 ['-SHELL', 'a', '1', 'b', '2', 'c', '3-']
222 ['-a', '1', 'b', '2', 'c', '3-']
223 ['-c', '3-']
224 ['--']
225 ['--']
226 ['-SHELL', 'a', '1', 'b', '2', 'c', '3-']
227 ['-a', '1', 'b', '2', 'c', '3-']
228 ['-c', '3-']
229 ['--']
230 ['--']
231 ['-SHELL a 1 b 2 c 3-']
232 ['-a 1 b 2 c 3-']
233 ['-c 3-']
234 ['--']
235 ['--']
236 ['-SHELL', 'a 1', 'b 2', 'c 3-']
237 ['-a 1', 'b 2', 'c 3-']
238 ['-c 3-']
239 ['--']
240 ['--']
241 ## END
242 ## N-I mksh status: 1
243 ## N-I mksh stdout-json: ""
244 ## BUG zsh stdout-json: ""
245
246 #### ${@:offset:length} and ${*:offset:length}
247 case $SH in (zsh) return ;; esac # zsh is very different
248
249 argv.shell-name-checked () {
250 argv.py "${@//$0/SHELL}"
251 }
252 fun() {
253 argv.shell-name-checked -${*:0:2}- # include $0
254 argv.shell-name-checked -${*:1:2}- # from $1
255 argv.shell-name-checked -${*:3:2}- # last parameter $3
256 argv.shell-name-checked -${*:4:2}- # empty
257 argv.shell-name-checked -${*:5:2}- # out of boundary
258 argv.shell-name-checked -${@:0:2}-
259 argv.shell-name-checked -${@:1:2}-
260 argv.shell-name-checked -${@:3:2}-
261 argv.shell-name-checked -${@:4:2}-
262 argv.shell-name-checked -${@:5:2}-
263 argv.shell-name-checked "-${*:0:2}-"
264 argv.shell-name-checked "-${*:1:2}-"
265 argv.shell-name-checked "-${*:3:2}-"
266 argv.shell-name-checked "-${*:4:2}-"
267 argv.shell-name-checked "-${*:5:2}-"
268 argv.shell-name-checked "-${@:0:2}-"
269 argv.shell-name-checked "-${@:1:2}-"
270 argv.shell-name-checked "-${@:3:2}-"
271 argv.shell-name-checked "-${@:4:2}-"
272 argv.shell-name-checked "-${@:5:2}-"
273 }
274 fun "a 1" "b 2" "c 3"
275 ## STDOUT:
276 ['-SHELL', 'a', '1-']
277 ['-a', '1', 'b', '2-']
278 ['-c', '3-']
279 ['--']
280 ['--']
281 ['-SHELL', 'a', '1-']
282 ['-a', '1', 'b', '2-']
283 ['-c', '3-']
284 ['--']
285 ['--']
286 ['-SHELL a 1-']
287 ['-a 1 b 2-']
288 ['-c 3-']
289 ['--']
290 ['--']
291 ['-SHELL', 'a 1-']
292 ['-a 1', 'b 2-']
293 ['-c 3-']
294 ['--']
295 ['--']
296 ## END
297 ## N-I mksh status: 1
298 ## N-I mksh stdout-json: ""
299 ## BUG zsh stdout-json: ""
300
301 #### ${@:0:1}
302 set a b c
303 result=$(echo ${@:0:1})
304 echo ${result//"$0"/'SHELL'}
305 ## STDOUT:
306 SHELL
307 ## END
308 ## N-I mksh STDOUT:
309
310 ## END
311
312 #### Permutations of implicit begin and length
313 array=(1 2 3)
314
315 argv.py ${array[@]}
316
317 # *** implicit length of N **
318 argv.py ${array[@]:0}
319
320 # Why is this one not allowed
321 #argv.py ${array[@]:}
322
323 # ** implicit length of ZERO **
324 #argv.py ${array[@]::}
325 #argv.py ${array[@]:0:}
326
327 argv.py ${array[@]:0:0}
328 echo
329
330 # Same agreed upon permutations
331 set -- 1 2 3
332 argv.py ${@}
333 argv.py ${@:1}
334 argv.py ${@:1:0}
335 echo
336
337 s='123'
338 argv.py "${s}"
339 argv.py "${s:0}"
340 argv.py "${s:0:0}"
341
342 ## STDOUT:
343 ['1', '2', '3']
344 ['1', '2', '3']
345 []
346
347 ['1', '2', '3']
348 ['1', '2', '3']
349 []
350
351 ['123']
352 ['123']
353 ['']
354 ## END
355
356 ## BUG mksh status: 1
357 ## BUG mksh STDOUT:
358 ['1', '2', '3']
359 ## END
360
361 #### ${array[@]:} vs ${array[@]: } - bash and zsh inconsistent
362
363 $SH -c 'array=(1 2 3); argv.py ${array[@]:}'
364 $SH -c 'array=(1 2 3); argv.py space ${array[@]: }'
365
366 $SH -c 's=123; argv.py ${s:}'
367 $SH -c 's=123; argv.py space ${s: }'
368
369 ## STDOUT:
370 ['space', '1', '2', '3']
371 ['space', '123']
372 ## END
373
374 ## OK osh STDOUT:
375 ['1', '2', '3']
376 ['space', '1', '2', '3']
377 ['123']
378 ['space', '123']
379 ## END
380
381 ## BUG mksh STDOUT:
382 ['space', '123']
383 ## END
384
385 #### ${array[@]::} has implicit length of zero - for ble.sh
386
387 # https://oilshell.zulipchat.com/#narrow/stream/121540-oil-discuss/topic/.24.7Barr.5B.40.5D.3A.3A.7D.20in.20bash.20-.20is.20it.20documented.3F
388
389 array=(1 2 3)
390 argv.py ${array[@]::}
391 argv.py ${array[@]:0:}
392
393 echo
394
395 set -- 1 2 3
396 argv.py ${@::}
397 argv.py ${@:0:}
398
399 ## status: 0
400 ## STDOUT:
401 []
402 []
403
404 []
405 []
406 ## END
407
408 ## OK mksh/zsh status: 1
409 ## OK mksh/zsh STDOUT:
410 ## END