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

470 lines, 231 significant
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
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#### Word elision with space
64s1=' '
65argv.py $s1
66## stdout: []
67
68#### Word elision with non-whitespace IFS
69# Treated differently than the default IFS. What is the rule here?
70IFS='_'
71char='_'
72space=' '
73empty=''
74argv.py $char
75argv.py $space
76argv.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.
90IFS=_
91s1='_a_b_'
92argv.py $s1
93## stdout: ['', 'a', 'b']
94
95#### Leading ' ' vs leading ' _ '
96# This behavior is weird, but all shells agree.
97IFS='_ '
98s1='_ a b _ '
99s2=' a b _ '
100argv.py $s1
101argv.py $s2
102## STDOUT:
103['', 'a', 'b']
104['a', 'b']
105## END
106
107#### Multiple non-whitespace IFS chars.
108IFS=_-
109s1='a__b---c_d'
110argv.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.
115IFS='_ '
116s1='a_b _ _ _ c _d e'
117argv.py $s1
118## stdout: ['a', 'b', '', '', 'c', 'd', 'e']
119
120#### empty $@ and $* is elided
121fun() { argv.py 1 $@ $* 2; }
122fun
123## stdout: ['1', '2']
124
125#### unquoted empty arg is elided
126empty=""
127argv.py 1 $empty 2
128## stdout: ['1', '2']
129
130#### unquoted whitespace arg is elided
131space=" "
132argv.py 1 $space 2
133## stdout: ['1', '2']
134
135#### empty literals are not elided
136space=" "
137argv.py 1 $space"" 2
138## stdout: ['1', '', '2']
139
140#### no splitting when IFS is empty
141IFS=""
142foo="a b"
143argv.py $foo
144## stdout: ['a b']
145
146#### default value can yield multiple words
147argv.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
151argv.py 1${undefined:-"2 3" "4 5"}6
152## stdout: ['12 3', '4 56']
153
154#### default value with unquoted IFS char
155IFS=_
156argv.py 1${undefined:-"2_3"x_x"4_5"}6
157## stdout: ['12_3x', 'x4_56']
158
159#### IFS empty doesn't do splitting
160IFS=''
161x=$(python2 -c 'print(" a b\tc\n")')
162argv.py $x
163## STDOUT:
164[' a b\tc']
165## END
166
167#### IFS unset behaves like $' \t\n'
168unset IFS
169x=$(python2 -c 'print(" a b\tc\n")')
170argv.py $x
171## STDOUT:
172['a', 'b', 'c']
173## END
174
175#### IFS='\'
176# NOTE: OSH fails this because of double backslash escaping issue!
177IFS='\'
178s='a\b'
179argv.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.
187IFS='\ '
188s='a\b \\ c d\'
189argv.py $s
190## STDOUT:
191['a', 'b', '', 'c', 'd']
192## END
193
194#### IFS characters are glob metacharacters
195IFS='* '
196s='a*b c'
197argv.py $s
198
199IFS='?'
200s='?x?y?z?'
201argv.py $s
202
203IFS='['
204s='[x[y[z['
205argv.py $s
206## STDOUT:
207['a', 'b', 'c']
208['', 'x', 'y', 'z']
209['', 'x', 'y', 'z']
210## END
211
212#### Trailing space
213argv.py 'Xec ho '
214argv.py X'ec ho '
215argv.py X"ec ho "
216## STDOUT:
217['Xec ho ']
218['Xec ho ']
219['Xec ho ']
220## END
221
222#### Empty IFS (regression for bug)
223IFS=
224echo ["$*"]
225set a b c
226echo ["$*"]
227## STDOUT:
228[]
229[abc]
230## END
231
232#### Unset IFS (regression for bug)
233set a b c
234unset IFS
235echo ["$*"]
236## STDOUT:
237[a b c]
238## END
239
240#### IFS=o (regression for bug)
241IFS=o
242echo hi
243## STDOUT:
244hi
245## END
246
247#### IFS and joining arrays
248IFS=:
249set -- x 'y z'
250argv.py "$@"
251argv.py $@
252argv.py "$*"
253argv.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
262IFS=:
263set -- x 'y z'
264
265s="$@"
266argv.py "$s"
267
268s=$@
269argv.py "$s"
270
271s"$*"
272argv.py "$s"
273
274s=$*
275argv.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
308empty=""
309space=" "
310AB="A B"
311X="X"
312Yspaces=" Y "
313
314
315#### IFS='' with $@ and $* (bug #627)
316set -- a 'b c'
317IFS=''
318argv.py at $@
319argv.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)
328set -- a 'b c'
329IFS=''
330printf '[%s]\n' $@
331printf '[%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)
340myarray=(a 'b c')
341IFS=''
342argv.py at ${myarray[@]}
343argv.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
353IFS=':'
354word='a:'
355argv.py ${word}:b
356argv.py ${word}:
357
358echo ---
359
360# Same thing happens for 'z'
361IFS='z'
362word='az'
363argv.py ${word}zb
364argv.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
374var='\'
375set -f
376echo $var
377## STDOUT:
378\
379## END
380
381#### Bug #1664, \\ with noglob
382
383# Note that we're not changing IFS
384
385argv.py [\\]_
386argv.py "[\\]_"
387
388# TODO: no difference observed here, go back to original bug
389
390#argv.py [\\_
391#argv.py "[\\_"
392
393echo noglob
394
395# repeat cases with -f, noglob
396set -f
397
398argv.py [\\]_
399argv.py "[\\]_"
400
401#argv.py [\\_
402#argv.py "[\\_"
403
404## STDOUT:
405['[\\]_']
406['[\\]_']
407noglob
408['[\\]_']
409['[\\]_']
410## END
411
412
413#### Empty IFS bug #2141 (from pnut)
414
415res=0
416sum() {
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
425unset IFS
426sum 12 30 # outputs "12 + 30 = 42"
427
428IFS=' '
429sum 12 30 # outputs "12 + 30 = 42"
430
431IFS=
432sum 12 30 # outputs "1230 + 0 = 1230"
433
434# I added this
435IFS=''
436sum 12 30
437
438set -u
439IFS=
440sum 12 30 # fails with "fatal: Undefined variable '2'" on res=$(($1 + $2))
441
442## STDOUT:
44312 + 30 = 42
44412 + 30 = 42
44512 + 30 = 42
44612 + 30 = 42
44712 + 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
455setopt SH_WORD_SPLIT
456
457x=çx IFS=ç
458printf "<%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