1 ## compare_shells: bash dash mksh ash yash
2 ## oils_failures_allowed: 6
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 #### $* with empty IFS
64 set -- "1 2" "3 4"
65
66 IFS=
67 argv.py $*
68 argv.py "$*"
69
70 ## STDOUT:
71 ['1 2', '3 4']
72 ['1 23 4']
73 ## END
74
75 #### Word elision with space
76 s1=' '
77 argv.py $s1
78 ## stdout: []
79
80 #### Word elision with non-whitespace IFS
81 # Treated differently than the default IFS. What is the rule here?
82 IFS='_'
83 char='_'
84 space=' '
85 empty=''
86 argv.py $char
87 argv.py $space
88 argv.py $empty
89 ## STDOUT:
90 ['']
91 [' ']
92 []
93 ## END
94 ## BUG yash STDOUT:
95 []
96 [' ']
97 []
98 ## END
99
100 #### Leading/trailing word elision with non-whitespace IFS
101 # This behavior is weird.
102 IFS=_
103 s1='_a_b_'
104 argv.py $s1
105 ## stdout: ['', 'a', 'b']
106
107 #### Leading ' ' vs leading ' _ '
108 # This behavior is weird, but all shells agree.
109 IFS='_ '
110 s1='_ a b _ '
111 s2=' a b _ '
112 argv.py $s1
113 argv.py $s2
114 ## STDOUT:
115 ['', 'a', 'b']
116 ['a', 'b']
117 ## END
118
119 #### Multiple non-whitespace IFS chars.
120 IFS=_-
121 s1='a__b---c_d'
122 argv.py $s1
123 ## stdout: ['a', '', 'b', '', '', 'c', 'd']
124
125 #### IFS with whitespace and non-whitepace.
126 # NOTE: Three delimiters means two empty words in the middle. No elision.
127 IFS='_ '
128 s1='a_b _ _ _ c _d e'
129 argv.py $s1
130 ## stdout: ['a', 'b', '', '', 'c', 'd', 'e']
131
132 #### empty $@ and $* is elided
133 fun() { argv.py 1 $@ $* 2; }
134 fun
135 ## stdout: ['1', '2']
136
137 #### unquoted empty arg is elided
138 empty=""
139 argv.py 1 $empty 2
140 ## stdout: ['1', '2']
141
142 #### unquoted whitespace arg is elided
143 space=" "
144 argv.py 1 $space 2
145 ## stdout: ['1', '2']
146
147 #### empty literals are not elided
148 space=" "
149 argv.py 1 $space"" 2
150 ## stdout: ['1', '', '2']
151
152 #### no splitting when IFS is empty
153 IFS=""
154 foo="a b"
155 argv.py $foo
156 ## stdout: ['a b']
157
158 #### default value can yield multiple words
159 argv.py 1 ${undefined:-"2 3" "4 5"} 6
160 ## stdout: ['1', '2 3', '4 5', '6']
161
162 #### default value can yield multiple words with part joining
163 argv.py 1${undefined:-"2 3" "4 5"}6
164 ## stdout: ['12 3', '4 56']
165
166 #### default value with unquoted IFS char
167 IFS=_
168 argv.py 1${undefined:-"2_3"x_x"4_5"}6
169 ## stdout: ['12_3x', 'x4_56']
170
171 #### IFS empty doesn't do splitting
172 IFS=''
173 x=$(python2 -c 'print(" a b\tc\n")')
174 argv.py $x
175 ## STDOUT:
176 [' a b\tc']
177 ## END
178
179 #### IFS unset behaves like $' \t\n'
180 unset IFS
181 x=$(python2 -c 'print(" a b\tc\n")')
182 argv.py $x
183 ## STDOUT:
184 ['a', 'b', 'c']
185 ## END
186
187 #### IFS='\'
188 # NOTE: OSH fails this because of double backslash escaping issue!
189 IFS='\'
190 s='a\b'
191 argv.py $s
192 ## STDOUT:
193 ['a', 'b']
194 ## END
195
196 #### IFS='\ '
197 # NOTE: OSH fails this because of double backslash escaping issue!
198 # When IFS is \, then you're no longer using backslash escaping.
199 IFS='\ '
200 s='a\b \\ c d\'
201 argv.py $s
202 ## STDOUT:
203 ['a', 'b', '', 'c', 'd']
204 ## END
205
206 #### IFS characters are glob metacharacters
207 IFS='* '
208 s='a*b c'
209 argv.py $s
210
211 IFS='?'
212 s='?x?y?z?'
213 argv.py $s
214
215 IFS='['
216 s='[x[y[z['
217 argv.py $s
218 ## STDOUT:
219 ['a', 'b', 'c']
220 ['', 'x', 'y', 'z']
221 ['', 'x', 'y', 'z']
222 ## END
223
224 #### Trailing space
225 argv.py 'Xec ho '
226 argv.py X'ec ho '
227 argv.py X"ec ho "
228 ## STDOUT:
229 ['Xec ho ']
230 ['Xec ho ']
231 ['Xec ho ']
232 ## END
233
234 #### Empty IFS (regression for bug)
235 IFS=
236 echo ["$*"]
237 set a b c
238 echo ["$*"]
239 ## STDOUT:
240 []
241 [abc]
242 ## END
243
244 #### Unset IFS (regression for bug)
245 set a b c
246 unset IFS
247 echo ["$*"]
248 ## STDOUT:
249 [a b c]
250 ## END
251
252 #### IFS=o (regression for bug)
253 IFS=o
254 echo hi
255 ## STDOUT:
256 hi
257 ## END
258
259 #### IFS and joining arrays
260 IFS=:
261 set -- x 'y z'
262 argv.py "$@"
263 argv.py $@
264 argv.py "$*"
265 argv.py $*
266 ## STDOUT:
267 ['x', 'y z']
268 ['x', 'y z']
269 ['x:y z']
270 ['x', 'y z']
271 ## END
272
273 #### IFS and joining arrays by assignments
274 IFS=:
275 set -- x 'y z'
276
277 s="$@"
278 argv.py "$s"
279
280 s=$@
281 argv.py "$s"
282
283 s"$*"
284 argv.py "$s"
285
286 s=$*
287 argv.py "$s"
288
289 # bash and mksh agree, but this doesn't really make sense to me.
290 # In OSH, "$@" is the only real array, so that's why it behaves differently.
291
292 ## STDOUT:
293 ['x y z']
294 ['x y z']
295 ['x y z']
296 ['x:y z']
297 ## END
298 ## BUG dash/ash/yash STDOUT:
299 ['x:y z']
300 ['x:y z']
301 ['x:y z']
302 ['x:y z']
303 ## END
304
305
306 # TODO:
307 # - unquoted args of whitespace are not elided (when IFS = null)
308 # - empty quoted args are kept
309 #
310 # - $* $@ with empty IFS
311 # - $* $@ with custom IFS
312 #
313 # - no splitting when IFS is empty
314 # - word splitting removes leading and trailing whitespace
315
316 # TODO: test framework needs common setup
317
318 # Test IFS and $@ $* on all these
319 #### TODO
320 empty=""
321 space=" "
322 AB="A B"
323 X="X"
324 Yspaces=" Y "
325
326
327 #### IFS='' with $@ and $* (bug #627)
328 set -- a 'b c'
329 IFS=''
330 argv.py at $@
331 argv.py star $*
332
333 # zsh agrees
334 ## STDOUT:
335 ['at', 'a', 'b c']
336 ['star', 'a', 'b c']
337 ## END
338
339 #### IFS='' with $@ and $* and printf (bug #627)
340 set -- a 'b c'
341 IFS=''
342 printf '[%s]\n' $@
343 printf '[%s]\n' $*
344 ## STDOUT:
345 [a]
346 [b c]
347 [a]
348 [b c]
349 ## END
350
351 #### IFS='' with ${a[@]} and ${a[*]} (bug #627)
352 case $SH in dash | ash) exit 0 ;; esac
353
354 myarray=(a 'b c')
355 IFS=''
356 argv.py at ${myarray[@]}
357 argv.py star ${myarray[*]}
358
359 ## STDOUT:
360 ['at', 'a', 'b c']
361 ['star', 'a', 'b c']
362 ## END
363 ## N-I dash/ash stdout-json: ""
364
365 #### IFS='' with ${!prefix@} and ${!prefix*} (bug #627)
366 case $SH in dash | mksh | ash | yash) exit 0 ;; esac
367
368 gLwbmGzS_var1=1
369 gLwbmGzS_var2=2
370 IFS=''
371 argv.py at ${!gLwbmGzS_@}
372 argv.py star ${!gLwbmGzS_*}
373
374 ## STDOUT:
375 ['at', 'gLwbmGzS_var1', 'gLwbmGzS_var2']
376 ['star', 'gLwbmGzS_var1', 'gLwbmGzS_var2']
377 ## END
378 ## BUG bash STDOUT:
379 ['at', 'gLwbmGzS_var1', 'gLwbmGzS_var2']
380 ['star', 'gLwbmGzS_var1gLwbmGzS_var2']
381 ## END
382 ## N-I dash/mksh/ash/yash stdout-json: ""
383
384 #### IFS='' with ${!a[@]} and ${!a[*]} (bug #627)
385 case $SH in dash | mksh | ash | yash) exit 0 ;; esac
386
387 IFS=''
388 a=(v1 v2 v3)
389 argv.py at ${!a[@]}
390 argv.py star ${!a[*]}
391
392 ## STDOUT:
393 ['at', '0', '1', '2']
394 ['star', '0', '1', '2']
395 ## END
396 ## BUG bash STDOUT:
397 ['at', '0', '1', '2']
398 ['star', '0 1 2']
399 ## END
400 ## N-I dash/mksh/ash/yash stdout-json: ""
401
402 #### Bug #628 split on : with : in literal word
403 IFS=':'
404 word='a:'
405 argv.py ${word}:b
406 argv.py ${word}:
407
408 echo ---
409
410 # Same thing happens for 'z'
411 IFS='z'
412 word='az'
413 argv.py ${word}zb
414 argv.py ${word}z
415 ## STDOUT:
416 ['a', ':b']
417 ['a', ':']
418 ---
419 ['a', 'zb']
420 ['a', 'z']
421 ## END
422
423 #### Bug #698, similar crash
424 var='\'
425 set -f
426 echo $var
427 ## STDOUT:
428 \
429 ## END
430
431 #### Bug #1664, \\ with noglob
432
433 # Note that we're not changing IFS
434
435 argv.py [\\]_
436 argv.py "[\\]_"
437
438 # TODO: no difference observed here, go back to original bug
439
440 #argv.py [\\_
441 #argv.py "[\\_"
442
443 echo noglob
444
445 # repeat cases with -f, noglob
446 set -f
447
448 argv.py [\\]_
449 argv.py "[\\]_"
450
451 #argv.py [\\_
452 #argv.py "[\\_"
453
454 ## STDOUT:
455 ['[\\]_']
456 ['[\\]_']
457 noglob
458 ['[\\]_']
459 ['[\\]_']
460 ## END
461
462
463 #### Empty IFS bug #2141 (from pnut)
464
465 res=0
466 sum() {
467 # implement callee-save calling convention using `set`
468 # here, we save the value of $res after the function parameters
469 set $@ $res # $1 $2 $3 are now set
470 res=$(($1 + $2))
471 echo "$1 + $2 = $res"
472 res=$3 # restore the value of $res
473 }
474
475 unset IFS
476 sum 12 30 # outputs "12 + 30 = 42"
477
478 IFS=' '
479 sum 12 30 # outputs "12 + 30 = 42"
480
481 IFS=
482 sum 12 30 # outputs "1230 + 0 = 1230"
483
484 # I added this
485 IFS=''
486 sum 12 30
487
488 set -u
489 IFS=
490 sum 12 30 # fails with "fatal: Undefined variable '2'" on res=$(($1 + $2))
491
492 ## STDOUT:
493 12 + 30 = 42
494 12 + 30 = 42
495 12 + 30 = 42
496 12 + 30 = 42
497 12 + 30 = 42
498 ## END
499
500 #### Unicode in IFS
501
502 # bash, zsh, and yash support unicode in IFS, but dash/mksh/ash don't.
503
504 # for zsh, though we're not testing it here
505 setopt SH_WORD_SPLIT
506
507 x=çx IFS=ç
508 printf "<%s>\n" $x
509
510 ## STDOUT:
511 <>
512 <x>
513 ## END
514
515 ## BUG dash/mksh/ash STDOUT:
516 <>
517 <>
518 <x>
519 ## END
520
521 #### 4 x 3 table: (default IFS, IFS='', IFS=zx) x ( $* "$*" $@ "$@" )
522
523 set -- 'a b' c ''
524
525 # default IFS
526 argv.py ' $* ' $*
527 argv.py ' "$*" ' "$*"
528 argv.py ' $@ ' $@
529 argv.py ' "$@" ' "$@"
530 echo
531
532 IFS=''
533 argv.py ' $* ' $*
534 argv.py ' "$*" ' "$*"
535 argv.py ' $@ ' $@
536 argv.py ' "$@" ' "$@"
537 echo
538
539 IFS=zx
540 argv.py ' $* ' $*
541 argv.py ' "$*" ' "$*"
542 argv.py ' $@ ' $@
543 argv.py ' "$@" ' "$@"
544
545 ## STDOUT:
546 [' $* ', 'a', 'b', 'c']
547 [' "$*" ', 'a b c ']
548 [' $@ ', 'a', 'b', 'c']
549 [' "$@" ', 'a b', 'c', '']
550
551 [' $* ', 'a b', 'c']
552 [' "$*" ', 'a bc']
553 [' $@ ', 'a b', 'c']
554 [' "$@" ', 'a b', 'c', '']
555
556 [' $* ', 'a b', 'c']
557 [' "$*" ', 'a bzcz']
558 [' $@ ', 'a b', 'c']
559 [' "$@" ', 'a b', 'c', '']
560 ## END
561
562 ## BUG yash STDOUT:
563 [' $* ', 'a', 'b', 'c', '']
564 [' "$*" ', 'a b c ']
565 [' $@ ', 'a', 'b', 'c', '']
566 [' "$@" ', 'a b', 'c', '']
567
568 [' $* ', 'a b', 'c', '']
569 [' "$*" ', 'a bc']
570 [' $@ ', 'a b', 'c', '']
571 [' "$@" ', 'a b', 'c', '']
572
573 [' $* ', 'a b', 'c', '']
574 [' "$*" ', 'a bzcz']
575 [' $@ ', 'a b', 'c', '']
576 [' "$@" ', 'a b', 'c', '']
577 ## END
578
579 #### 4 x 3 table - with for loop
580 case $SH in yash) exit ;; esac # no echo -n
581
582 set -- 'a b' c ''
583
584 # default IFS
585 echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
586 echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
587 echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
588 echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
589 echo
590
591 IFS=''
592 echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
593 echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
594 echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
595 echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
596 echo
597
598 IFS=zx
599 echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
600 echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
601 echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
602 echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
603
604 ## STDOUT:
605 $* -a- -b- -c-
606 "$*" -a b c -
607 $@ -a- -b- -c-
608 "$@" -a b- -c- --
609
610 $* -a b- -c-
611 "$*" -a bc-
612 $@ -a b- -c-
613 "$@" -a b- -c- --
614
615 $* -a b- -c-
616 "$*" -a b c -
617 $@ -a b- -c-
618 "$@" -a b- -c- --
619 ## END
620 ## N-I yash STDOUT:
621 ## END
622
623 #### IFS=x and '' and $@ - same bug as spec/toysh-posix case #12
624
625 case $SH in yash) exit ;; esac # no echo -n
626
627 set -- one '' two
628
629 IFS=zx
630 echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
631 echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
632 echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
633 echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
634
635 argv.py ' $* ' $*
636 argv.py ' "$*" ' "$*"
637 argv.py ' $@ ' $@
638 argv.py ' "$@" ' "$@"
639
640
641 ## STDOUT:
642 $* -one- -- -two-
643 "$*" -one two-
644 $@ -one- -- -two-
645 "$@" -one- -- -two-
646 [' $* ', 'one', '', 'two']
647 [' "$*" ', 'onezztwo']
648 [' $@ ', 'one', '', 'two']
649 [' "$@" ', 'one', '', 'two']
650 ## END
651
652 ## BUG dash/ash STDOUT:
653 $* -one- -two-
654 "$*" -one two-
655 $@ -one- -two-
656 "$@" -one- -- -two-
657 [' $* ', 'one', 'two']
658 [' "$*" ', 'onezztwo']
659 [' $@ ', 'one', 'two']
660 [' "$@" ', 'one', '', 'two']
661 ## END
662
663 ## N-I yash STDOUT:
664 ## END