1 ## compare_shells: bash dash mksh ash yash
2 ## oils_failures_allowed: 10
3
4 # NOTE on bash bug: After setting IFS to array, it never splits anymore? Even
5 # if you assign IFS again.
6
7 #### IFS is scoped
8 IFS=b
9 word=abcd
10 f() { local IFS=c; argv.py $word; }
11 f
12 argv.py $word
13 ## stdout-json: "['ab', 'd']\n['a', 'cd']\n"
14
15 #### Tilde sub is not split, but var sub is
16 HOME="foo bar"
17 argv.py ~
18 argv.py $HOME
19 ## stdout-json: "['foo bar']\n['foo', 'bar']\n"
20
21 #### Word splitting
22 a="1 2"
23 b="3 4"
24 argv.py $a"$b"
25 ## stdout-json: "['1', '23 4']\n"
26
27 #### Word splitting 2
28 a="1 2"
29 b="3 4"
30 c="5 6"
31 d="7 8"
32 argv.py $a"$b"$c"$d"
33 ## stdout-json: "['1', '23 45', '67 8']\n"
34
35 # Has tests on differences between $* "$*" $@ "$@"
36 # http://stackoverflow.com/questions/448407/bash-script-to-receive-and-repass-quoted-parameters
37
38 #### $*
39 fun() { argv.py -$*-; }
40 fun "a 1" "b 2" "c 3"
41 ## stdout: ['-a', '1', 'b', '2', 'c', '3-']
42
43 #### "$*"
44 fun() { argv.py "-$*-"; }
45 fun "a 1" "b 2" "c 3"
46 ## stdout: ['-a 1 b 2 c 3-']
47
48 #### $@
49 # How does this differ from $* ? I don't think it does.
50 fun() { argv.py -$@-; }
51 fun "a 1" "b 2" "c 3"
52 ## stdout: ['-a', '1', 'b', '2', 'c', '3-']
53
54 #### "$@"
55 fun() { argv.py "-$@-"; }
56 fun "a 1" "b 2" "c 3"
57 ## stdout: ['-a 1', 'b 2', 'c 3-']
58
59 #### empty argv
60 argv.py 1 "$@" 2 $@ 3 "$*" 4 $* 5
61 ## stdout: ['1', '2', '3', '', '4', '5']
62
63 #### Word elision with space
64 s1=' '
65 argv.py $s1
66 ## stdout: []
67
68 #### Word elision with non-whitespace IFS
69 # Treated differently than the default IFS. What is the rule here?
70 IFS='_'
71 char='_'
72 space=' '
73 empty=''
74 argv.py $char
75 argv.py $space
76 argv.py $empty
77 ## STDOUT:
78 ['']
79 [' ']
80 []
81 ## END
82 ## BUG yash STDOUT:
83 []
84 [' ']
85 []
86 ## END
87
88 #### Leading/trailing word elision with non-whitespace IFS
89 # This behavior is weird.
90 IFS=_
91 s1='_a_b_'
92 argv.py $s1
93 ## stdout: ['', 'a', 'b']
94
95 #### Leading ' ' vs leading ' _ '
96 # This behavior is weird, but all shells agree.
97 IFS='_ '
98 s1='_ a b _ '
99 s2=' a b _ '
100 argv.py $s1
101 argv.py $s2
102 ## STDOUT:
103 ['', 'a', 'b']
104 ['a', 'b']
105 ## END
106
107 #### Multiple non-whitespace IFS chars.
108 IFS=_-
109 s1='a__b---c_d'
110 argv.py $s1
111 ## stdout: ['a', '', 'b', '', '', 'c', 'd']
112
113 #### IFS with whitespace and non-whitepace.
114 # NOTE: Three delimiters means two empty words in the middle. No elision.
115 IFS='_ '
116 s1='a_b _ _ _ c _d e'
117 argv.py $s1
118 ## stdout: ['a', 'b', '', '', 'c', 'd', 'e']
119
120 #### empty $@ and $* is elided
121 fun() { argv.py 1 $@ $* 2; }
122 fun
123 ## stdout: ['1', '2']
124
125 #### unquoted empty arg is elided
126 empty=""
127 argv.py 1 $empty 2
128 ## stdout: ['1', '2']
129
130 #### unquoted whitespace arg is elided
131 space=" "
132 argv.py 1 $space 2
133 ## stdout: ['1', '2']
134
135 #### empty literals are not elided
136 space=" "
137 argv.py 1 $space"" 2
138 ## stdout: ['1', '', '2']
139
140 #### no splitting when IFS is empty
141 IFS=""
142 foo="a b"
143 argv.py $foo
144 ## stdout: ['a b']
145
146 #### default value can yield multiple words
147 argv.py 1 ${undefined:-"2 3" "4 5"} 6
148 ## stdout: ['1', '2 3', '4 5', '6']
149
150 #### default value can yield multiple words with part joining
151 argv.py 1${undefined:-"2 3" "4 5"}6
152 ## stdout: ['12 3', '4 56']
153
154 #### default value with unquoted IFS char
155 IFS=_
156 argv.py 1${undefined:-"2_3"x_x"4_5"}6
157 ## stdout: ['12_3x', 'x4_56']
158
159 #### IFS empty doesn't do splitting
160 IFS=''
161 x=$(python2 -c 'print(" a b\tc\n")')
162 argv.py $x
163 ## STDOUT:
164 [' a b\tc']
165 ## END
166
167 #### IFS unset behaves like $' \t\n'
168 unset IFS
169 x=$(python2 -c 'print(" a b\tc\n")')
170 argv.py $x
171 ## STDOUT:
172 ['a', 'b', 'c']
173 ## END
174
175 #### IFS='\'
176 # NOTE: OSH fails this because of double backslash escaping issue!
177 IFS='\'
178 s='a\b'
179 argv.py $s
180 ## STDOUT:
181 ['a', 'b']
182 ## END
183
184 #### IFS='\ '
185 # NOTE: OSH fails this because of double backslash escaping issue!
186 # When IFS is \, then you're no longer using backslash escaping.
187 IFS='\ '
188 s='a\b \\ c d\'
189 argv.py $s
190 ## STDOUT:
191 ['a', 'b', '', 'c', 'd']
192 ## END
193
194 #### IFS characters are glob metacharacters
195 IFS='* '
196 s='a*b c'
197 argv.py $s
198
199 IFS='?'
200 s='?x?y?z?'
201 argv.py $s
202
203 IFS='['
204 s='[x[y[z['
205 argv.py $s
206 ## STDOUT:
207 ['a', 'b', 'c']
208 ['', 'x', 'y', 'z']
209 ['', 'x', 'y', 'z']
210 ## END
211
212 #### Trailing space
213 argv.py 'Xec ho '
214 argv.py X'ec ho '
215 argv.py X"ec ho "
216 ## STDOUT:
217 ['Xec ho ']
218 ['Xec ho ']
219 ['Xec ho ']
220 ## END
221
222 #### Empty IFS (regression for bug)
223 IFS=
224 echo ["$*"]
225 set a b c
226 echo ["$*"]
227 ## STDOUT:
228 []
229 [abc]
230 ## END
231
232 #### Unset IFS (regression for bug)
233 set a b c
234 unset IFS
235 echo ["$*"]
236 ## STDOUT:
237 [a b c]
238 ## END
239
240 #### IFS=o (regression for bug)
241 IFS=o
242 echo hi
243 ## STDOUT:
244 hi
245 ## END
246
247 #### IFS and joining arrays
248 IFS=:
249 set -- x 'y z'
250 argv.py "$@"
251 argv.py $@
252 argv.py "$*"
253 argv.py $*
254 ## STDOUT:
255 ['x', 'y z']
256 ['x', 'y z']
257 ['x:y z']
258 ['x', 'y z']
259 ## END
260
261 #### IFS and joining arrays by assignments
262 IFS=:
263 set -- x 'y z'
264
265 s="$@"
266 argv.py "$s"
267
268 s=$@
269 argv.py "$s"
270
271 s"$*"
272 argv.py "$s"
273
274 s=$*
275 argv.py "$s"
276
277 # bash and mksh agree, but this doesn't really make sense to me.
278 # In OSH, "$@" is the only real array, so that's why it behaves differently.
279
280 ## STDOUT:
281 ['x y z']
282 ['x y z']
283 ['x y z']
284 ['x:y z']
285 ## END
286 ## BUG dash/ash/yash STDOUT:
287 ['x:y z']
288 ['x:y z']
289 ['x:y z']
290 ['x:y z']
291 ## END
292
293
294 # TODO:
295 # - unquoted args of whitespace are not elided (when IFS = null)
296 # - empty quoted args are kept
297 #
298 # - $* $@ with empty IFS
299 # - $* $@ with custom IFS
300 #
301 # - no splitting when IFS is empty
302 # - word splitting removes leading and trailing whitespace
303
304 # TODO: test framework needs common setup
305
306 # Test IFS and $@ $* on all these
307 #### TODO
308 empty=""
309 space=" "
310 AB="A B"
311 X="X"
312 Yspaces=" Y "
313
314
315 #### IFS='' with $@ and $* (bug #627)
316 set -- a 'b c'
317 IFS=''
318 argv.py at $@
319 argv.py star $*
320
321 # zsh agrees
322 ## STDOUT:
323 ['at', 'a', 'b c']
324 ['star', 'a', 'b c']
325 ## END
326
327 #### IFS='' with $@ and $* and printf (bug #627)
328 set -- a 'b c'
329 IFS=''
330 printf '[%s]\n' $@
331 printf '[%s]\n' $*
332 ## STDOUT:
333 [a]
334 [b c]
335 [a]
336 [b c]
337 ## END
338
339 #### IFS='' with ${a[@]} and ${a[*]} (bug #627)
340 myarray=(a 'b c')
341 IFS=''
342 argv.py at ${myarray[@]}
343 argv.py star ${myarray[*]}
344
345 ## STDOUT:
346 ['at', 'a', 'b c']
347 ['star', 'a', 'b c']
348 ## END
349 ## N-I dash/ash status: 2
350 ## N-I dash/ash stdout-json: ""
351
352 #### Bug #628 split on : with : in literal word
353 IFS=':'
354 word='a:'
355 argv.py ${word}:b
356 argv.py ${word}:
357
358 echo ---
359
360 # Same thing happens for 'z'
361 IFS='z'
362 word='az'
363 argv.py ${word}zb
364 argv.py ${word}z
365 ## STDOUT:
366 ['a', ':b']
367 ['a', ':']
368 ---
369 ['a', 'zb']
370 ['a', 'z']
371 ## END
372
373 #### Bug #698, similar crash
374 var='\'
375 set -f
376 echo $var
377 ## STDOUT:
378 \
379 ## END
380
381 #### Bug #1664, \\ with noglob
382
383 # Note that we're not changing IFS
384
385 argv.py [\\]_
386 argv.py "[\\]_"
387
388 # TODO: no difference observed here, go back to original bug
389
390 #argv.py [\\_
391 #argv.py "[\\_"
392
393 echo noglob
394
395 # repeat cases with -f, noglob
396 set -f
397
398 argv.py [\\]_
399 argv.py "[\\]_"
400
401 #argv.py [\\_
402 #argv.py "[\\_"
403
404 ## STDOUT:
405 ['[\\]_']
406 ['[\\]_']
407 noglob
408 ['[\\]_']
409 ['[\\]_']
410 ## END
411
412
413 #### Empty IFS bug #2141 (from pnut)
414
415 res=0
416 sum() {
417 # implement callee-save calling convention using `set`
418 # here, we save the value of $res after the function parameters
419 set $@ $res # $1 $2 $3 are now set
420 res=$(($1 + $2))
421 echo "$1 + $2 = $res"
422 res=$3 # restore the value of $res
423 }
424
425 unset IFS
426 sum 12 30 # outputs "12 + 30 = 42"
427
428 IFS=' '
429 sum 12 30 # outputs "12 + 30 = 42"
430
431 IFS=
432 sum 12 30 # outputs "1230 + 0 = 1230"
433
434 # I added this
435 IFS=''
436 sum 12 30
437
438 set -u
439 IFS=
440 sum 12 30 # fails with "fatal: Undefined variable '2'" on res=$(($1 + $2))
441
442 ## STDOUT:
443 12 + 30 = 42
444 12 + 30 = 42
445 12 + 30 = 42
446 12 + 30 = 42
447 12 + 30 = 42
448 ## END
449
450 #### Unicode in IFS
451
452 # bash, zsh, and yash support unicode in IFS, but dash/mksh/ash don't.
453
454 # for zsh, though we're not testing it here
455 setopt SH_WORD_SPLIT
456
457 x=çx IFS=ç
458 printf "<%s>\n" $x
459
460 ## STDOUT:
461 <>
462 <x>
463 ## END
464
465 ## BUG dash/mksh/ash STDOUT:
466 <>
467 <>
468 <x>
469 ## END
470