OILS / spec / word-split.test.sh View on Github | oils.pub

664 lines, 355 significant
1## compare_shells: bash dash mksh ash yash
2## oils_failures_allowed: 7
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
8IFS=b
9word=abcd
10f() { local IFS=c; argv.py $word; }
11f
12argv.py $word
13## stdout-json: "['ab', 'd']\n['a', 'cd']\n"
14
15#### Tilde sub is not split, but var sub is
16HOME="foo bar"
17argv.py ~
18argv.py $HOME
19## stdout-json: "['foo bar']\n['foo', 'bar']\n"
20
21#### Word splitting
22a="1 2"
23b="3 4"
24argv.py $a"$b"
25## stdout-json: "['1', '23 4']\n"
26
27#### Word splitting 2
28a="1 2"
29b="3 4"
30c="5 6"
31d="7 8"
32argv.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#### $*
39fun() { argv.py -$*-; }
40fun "a 1" "b 2" "c 3"
41## stdout: ['-a', '1', 'b', '2', 'c', '3-']
42
43#### "$*"
44fun() { argv.py "-$*-"; }
45fun "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.
50fun() { argv.py -$@-; }
51fun "a 1" "b 2" "c 3"
52## stdout: ['-a', '1', 'b', '2', 'c', '3-']
53
54#### "$@"
55fun() { argv.py "-$@-"; }
56fun "a 1" "b 2" "c 3"
57## stdout: ['-a 1', 'b 2', 'c 3-']
58
59#### empty argv
60argv.py 1 "$@" 2 $@ 3 "$*" 4 $* 5
61## stdout: ['1', '2', '3', '', '4', '5']
62
63#### $* with empty IFS
64set -- "1 2" "3 4"
65
66IFS=
67argv.py $*
68argv.py "$*"
69
70## STDOUT:
71['1 2', '3 4']
72['1 23 4']
73## END
74
75#### Word elision with space
76s1=' '
77argv.py $s1
78## stdout: []
79
80#### Word elision with non-whitespace IFS
81# Treated differently than the default IFS. What is the rule here?
82IFS='_'
83char='_'
84space=' '
85empty=''
86argv.py $char
87argv.py $space
88argv.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.
102IFS=_
103s1='_a_b_'
104argv.py $s1
105## stdout: ['', 'a', 'b']
106
107#### Leading ' ' vs leading ' _ '
108# This behavior is weird, but all shells agree.
109IFS='_ '
110s1='_ a b _ '
111s2=' a b _ '
112argv.py $s1
113argv.py $s2
114## STDOUT:
115['', 'a', 'b']
116['a', 'b']
117## END
118
119#### Multiple non-whitespace IFS chars.
120IFS=_-
121s1='a__b---c_d'
122argv.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.
127IFS='_ '
128s1='a_b _ _ _ c _d e'
129argv.py $s1
130## stdout: ['a', 'b', '', '', 'c', 'd', 'e']
131
132#### empty $@ and $* is elided
133fun() { argv.py 1 $@ $* 2; }
134fun
135## stdout: ['1', '2']
136
137#### unquoted empty arg is elided
138empty=""
139argv.py 1 $empty 2
140## stdout: ['1', '2']
141
142#### unquoted whitespace arg is elided
143space=" "
144argv.py 1 $space 2
145## stdout: ['1', '2']
146
147#### empty literals are not elided
148space=" "
149argv.py 1 $space"" 2
150## stdout: ['1', '', '2']
151
152#### no splitting when IFS is empty
153IFS=""
154foo="a b"
155argv.py $foo
156## stdout: ['a b']
157
158#### default value can yield multiple words
159argv.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
163argv.py 1${undefined:-"2 3" "4 5"}6
164## stdout: ['12 3', '4 56']
165
166#### default value with unquoted IFS char
167IFS=_
168argv.py 1${undefined:-"2_3"x_x"4_5"}6
169## stdout: ['12_3x', 'x4_56']
170
171#### IFS empty doesn't do splitting
172IFS=''
173x=$(python2 -c 'print(" a b\tc\n")')
174argv.py $x
175## STDOUT:
176[' a b\tc']
177## END
178
179#### IFS unset behaves like $' \t\n'
180unset IFS
181x=$(python2 -c 'print(" a b\tc\n")')
182argv.py $x
183## STDOUT:
184['a', 'b', 'c']
185## END
186
187#### IFS='\'
188# NOTE: OSH fails this because of double backslash escaping issue!
189IFS='\'
190s='a\b'
191argv.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.
199IFS='\ '
200s='a\b \\ c d\'
201argv.py $s
202## STDOUT:
203['a', 'b', '', 'c', 'd']
204## END
205
206#### IFS characters are glob metacharacters
207IFS='* '
208s='a*b c'
209argv.py $s
210
211IFS='?'
212s='?x?y?z?'
213argv.py $s
214
215IFS='['
216s='[x[y[z['
217argv.py $s
218## STDOUT:
219['a', 'b', 'c']
220['', 'x', 'y', 'z']
221['', 'x', 'y', 'z']
222## END
223
224#### Trailing space
225argv.py 'Xec ho '
226argv.py X'ec ho '
227argv.py X"ec ho "
228## STDOUT:
229['Xec ho ']
230['Xec ho ']
231['Xec ho ']
232## END
233
234#### Empty IFS (regression for bug)
235IFS=
236echo ["$*"]
237set a b c
238echo ["$*"]
239## STDOUT:
240[]
241[abc]
242## END
243
244#### Unset IFS (regression for bug)
245set a b c
246unset IFS
247echo ["$*"]
248## STDOUT:
249[a b c]
250## END
251
252#### IFS=o (regression for bug)
253IFS=o
254echo hi
255## STDOUT:
256hi
257## END
258
259#### IFS and joining arrays
260IFS=:
261set -- x 'y z'
262argv.py "$@"
263argv.py $@
264argv.py "$*"
265argv.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
274IFS=:
275set -- x 'y z'
276
277s="$@"
278argv.py "$s"
279
280s=$@
281argv.py "$s"
282
283s"$*"
284argv.py "$s"
285
286s=$*
287argv.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
320empty=""
321space=" "
322AB="A B"
323X="X"
324Yspaces=" Y "
325
326
327#### IFS='' with $@ and $* (bug #627)
328set -- a 'b c'
329IFS=''
330argv.py at $@
331argv.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)
340set -- a 'b c'
341IFS=''
342printf '[%s]\n' $@
343printf '[%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)
352case $SH in dash | ash) exit 0 ;; esac
353
354myarray=(a 'b c')
355IFS=''
356argv.py at ${myarray[@]}
357argv.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)
366case $SH in dash | mksh | ash | yash) exit 0 ;; esac
367
368gLwbmGzS_var1=1
369gLwbmGzS_var2=2
370IFS=''
371argv.py at ${!gLwbmGzS_@}
372argv.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)
385case $SH in dash | mksh | ash | yash) exit 0 ;; esac
386
387IFS=''
388a=(v1 v2 v3)
389argv.py at ${!a[@]}
390argv.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
403IFS=':'
404word='a:'
405argv.py ${word}:b
406argv.py ${word}:
407
408echo ---
409
410# Same thing happens for 'z'
411IFS='z'
412word='az'
413argv.py ${word}zb
414argv.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
424var='\'
425set -f
426echo $var
427## STDOUT:
428\
429## END
430
431#### Bug #1664, \\ with noglob
432
433# Note that we're not changing IFS
434
435argv.py [\\]_
436argv.py "[\\]_"
437
438# TODO: no difference observed here, go back to original bug
439
440#argv.py [\\_
441#argv.py "[\\_"
442
443echo noglob
444
445# repeat cases with -f, noglob
446set -f
447
448argv.py [\\]_
449argv.py "[\\]_"
450
451#argv.py [\\_
452#argv.py "[\\_"
453
454## STDOUT:
455['[\\]_']
456['[\\]_']
457noglob
458['[\\]_']
459['[\\]_']
460## END
461
462
463#### Empty IFS bug #2141 (from pnut)
464
465res=0
466sum() {
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
475unset IFS
476sum 12 30 # outputs "12 + 30 = 42"
477
478IFS=' '
479sum 12 30 # outputs "12 + 30 = 42"
480
481IFS=
482sum 12 30 # outputs "1230 + 0 = 1230"
483
484# I added this
485IFS=''
486sum 12 30
487
488set -u
489IFS=
490sum 12 30 # fails with "fatal: Undefined variable '2'" on res=$(($1 + $2))
491
492## STDOUT:
49312 + 30 = 42
49412 + 30 = 42
49512 + 30 = 42
49612 + 30 = 42
49712 + 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
505setopt SH_WORD_SPLIT
506
507x=çx IFS=ç
508printf "<%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
523set -- 'a b' c ''
524
525# default IFS
526argv.py ' $* ' $*
527argv.py ' "$*" ' "$*"
528argv.py ' $@ ' $@
529argv.py ' "$@" ' "$@"
530echo
531
532IFS=''
533argv.py ' $* ' $*
534argv.py ' "$*" ' "$*"
535argv.py ' $@ ' $@
536argv.py ' "$@" ' "$@"
537echo
538
539IFS=zx
540argv.py ' $* ' $*
541argv.py ' "$*" ' "$*"
542argv.py ' $@ ' $@
543argv.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
580case $SH in yash) exit ;; esac # no echo -n
581
582set -- 'a b' c ''
583
584# default IFS
585echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
586echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
587echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
588echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
589echo
590
591IFS=''
592echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
593echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
594echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
595echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
596echo
597
598IFS=zx
599echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
600echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
601echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
602echo -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
625case $SH in yash) exit ;; esac # no echo -n
626
627set -- one '' two
628
629IFS=zx
630echo -n ' $* '; for i in $*; do echo -n ' '; echo -n -$i-; done; echo
631echo -n ' "$*" '; for i in "$*"; do echo -n ' '; echo -n -$i-; done; echo
632echo -n ' $@ '; for i in $@; do echo -n ' '; echo -n -$i-; done; echo
633echo -n ' "$@" '; for i in "$@"; do echo -n ' '; echo -n -$i-; done; echo
634
635argv.py ' $* ' $*
636argv.py ' "$*" ' "$*"
637argv.py ' $@ ' $@
638argv.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