1 #!/usr/bin/env bash
2
3 # NOTE on bash bug: After setting IFS to array, it never splits anymore? Even
4 # if you assign IFS again.
5
6 #### IFS is scoped
7 IFS=b
8 word=abcd
9 f() { local IFS=c; argv.py $word; }
10 f
11 argv.py $word
12 ## stdout-json: "['ab', 'd']\n['a', 'cd']\n"
13
14 #### Tilde sub is not split, but var sub is
15 HOME="foo bar"
16 argv.py ~
17 argv.py $HOME
18 ## stdout-json: "['foo bar']\n['foo', 'bar']\n"
19
20 #### Word splitting
21 a="1 2"
22 b="3 4"
23 argv.py $a"$b"
24 ## stdout-json: "['1', '23 4']\n"
25
26 #### Word splitting 2
27 a="1 2"
28 b="3 4"
29 c="5 6"
30 d="7 8"
31 argv.py $a"$b"$c"$d"
32 ## stdout-json: "['1', '23 45', '67 8']\n"
33
34 # Has tests on differences between $* "$*" $@ "$@"
35 # http://stackoverflow.com/questions/448407/bash-script-to-receive-and-repass-quoted-parameters
36
37 #### $*
38 func() { argv.py -$*-; }
39 func "a 1" "b 2" "c 3"
40 ## stdout: ['-a', '1', 'b', '2', 'c', '3-']
41
42 #### "$*"
43 func() { argv.py "-$*-"; }
44 func "a 1" "b 2" "c 3"
45 ## stdout: ['-a 1 b 2 c 3-']
46
47 #### $@
48 # How does this differ from $* ? I don't think it does.
49 func() { argv.py -$@-; }
50 func "a 1" "b 2" "c 3"
51 ## stdout: ['-a', '1', 'b', '2', 'c', '3-']
52
53 #### "$@"
54 func() { argv.py "-$@-"; }
55 func "a 1" "b 2" "c 3"
56 ## stdout: ['-a 1', 'b 2', 'c 3-']
57
58 #### empty argv
59 argv.py 1 "$@" 2 $@ 3 "$*" 4 $* 5
60 ## stdout: ['1', '2', '3', '', '4', '5']
61
62 #### Word elision with space
63 s1=' '
64 argv.py $s1
65 ## stdout: []
66
67 #### Word elision with non-whitespace IFS
68 # Treated differently than the default IFS. What is the rule here?
69 IFS='_'
70 char='_'
71 space=' '
72 empty=''
73 argv.py $char
74 argv.py $space
75 argv.py $empty
76 ## STDOUT:
77 ['']
78 [' ']
79 []
80 ## END
81
82 #### Leading/trailing word elision with non-whitespace IFS
83 # This behavior is weird.
84 IFS=_
85 s1='_a_b_'
86 argv.py $s1
87 ## stdout: ['', 'a', 'b']
88
89 #### Leading ' ' vs leading ' _ '
90 # This behavior is weird, but all shells agree.
91 IFS='_ '
92 s1='_ a b _ '
93 s2=' a b _ '
94 argv.py $s1
95 argv.py $s2
96 ## STDOUT:
97 ['', 'a', 'b']
98 ['a', 'b']
99 ## END
100
101 #### Multiple non-whitespace IFS chars.
102 IFS=_-
103 s1='a__b---c_d'
104 argv.py $s1
105 ## stdout: ['a', '', 'b', '', '', 'c', 'd']
106
107 #### IFS with whitespace and non-whitepace.
108 # NOTE: Three delimiters means two empty words in the middle. No elision.
109 IFS='_ '
110 s1='a_b _ _ _ c _d e'
111 argv.py $s1
112 ## stdout: ['a', 'b', '', '', 'c', 'd', 'e']
113
114 #### empty $@ and $* is elided
115 func() { argv.py 1 $@ $* 2; }
116 func
117 ## stdout: ['1', '2']
118
119 #### unquoted empty arg is elided
120 empty=""
121 argv.py 1 $empty 2
122 ## stdout: ['1', '2']
123
124 #### unquoted whitespace arg is elided
125 space=" "
126 argv.py 1 $space 2
127 ## stdout: ['1', '2']
128
129 #### empty literals are not elided
130 space=" "
131 argv.py 1 $space"" 2
132 ## stdout: ['1', '', '2']
133
134 #### no splitting when IFS is empty
135 IFS=""
136 foo="a b"
137 argv.py $foo
138 ## stdout: ['a b']
139
140 #### default value can yield multiple words
141 argv.py 1 ${undefined:-"2 3" "4 5"} 6
142 ## stdout: ['1', '2 3', '4 5', '6']
143
144 #### default value can yield multiple words with part joining
145 argv.py 1${undefined:-"2 3" "4 5"}6
146 ## stdout: ['12 3', '4 56']
147
148 #### default value with unquoted IFS char
149 IFS=_
150 argv.py 1${undefined:-"2_3"x_x"4_5"}6
151 ## stdout: ['12_3x', 'x4_56']
152
153 #### IFS empty doesn't do splitting
154 IFS=''
155 x=$(echo -e ' a b\tc\n')
156 argv.py $x
157 ## STDOUT:
158 [' a b\tc']
159 ## END
160 ## N-I dash STDOUT:
161 ['-e a b\tc']
162 ## END
163
164
165 #### IFS unset behaves like $' \t\n'
166 unset IFS
167 x=$(echo -e ' a b\tc\n')
168 argv.py $x
169 ## STDOUT:
170 ['a', 'b', 'c']
171 ## END
172 ## N-I dash STDOUT:
173 ['-e', 'a', 'b', 'c']
174 ## END
175
176 #### IFS='\'
177 # NOTE: OSH fails this because of double backslash escaping issue!
178 IFS='\'
179 s='a\b'
180 argv.py $s
181 ## STDOUT:
182 ['a', 'b']
183 ## END
184
185 #### IFS='\ '
186 # NOTE: OSH fails this because of double backslash escaping issue!
187 # When IFS is \, then you're no longer using backslash escaping.
188 IFS='\ '
189 s='a\b \\ c d\'
190 argv.py $s
191 ## STDOUT:
192 ['a', 'b', '', 'c', 'd']
193 ## END
194
195 #### IFS characters are glob metacharacters
196 IFS='* '
197 s='a*b c'
198 argv.py $s
199
200 IFS='?'
201 s='?x?y?z?'
202 argv.py $s
203
204 IFS='['
205 s='[x[y[z['
206 argv.py $s
207 ## STDOUT:
208 ['a', 'b', 'c']
209 ['', 'x', 'y', 'z']
210 ['', 'x', 'y', 'z']
211 ## END
212
213 #### Trailing space
214 argv.py 'Xec ho '
215 argv.py X'ec ho '
216 argv.py X"ec ho "
217 ## STDOUT:
218 ['Xec ho ']
219 ['Xec ho ']
220 ['Xec ho ']
221 ## END
222
223 #### Empty IFS (regression for bug)
224 IFS=
225 echo ["$*"]
226 set a b c
227 echo ["$*"]
228 ## STDOUT:
229 []
230 [abc]
231 ## END
232
233 #### Unset IFS (regression for bug)
234 set a b c
235 unset IFS
236 echo ["$*"]
237 ## STDOUT:
238 [a b c]
239 ## END
240
241 #### IFS=o (regression for bug)
242 IFS=o
243 echo hi
244 ## STDOUT:
245 hi
246 ## END
247
248 #### IFS and joining arrays
249 IFS=:
250 set -- x 'y z'
251 argv.py "$@"
252 argv.py $@
253 argv.py "$*"
254 argv.py $*
255 ## STDOUT:
256 ['x', 'y z']
257 ['x', 'y z']
258 ['x:y z']
259 ['x', 'y z']
260 ## END
261
262 #### IFS and joining arrays by assignments
263 IFS=:
264 set -- x 'y z'
265
266 s="$@"
267 argv.py "$s"
268
269 s=$@
270 argv.py "$s"
271
272 s"$*"
273 argv.py "$s"
274
275 s=$*
276 argv.py "$s"
277
278 # bash and mksh agree, but this doesn't really make sense to me.
279 # In OSH, "$@" is the only real array, so that's why it behaves differently.
280
281 ## STDOUT:
282 ['x y z']
283 ['x y z']
284 ['x y z']
285 ['x:y z']
286 ## END
287 ## OK dash STDOUT:
288 ['x:y z']
289 ['x:y z']
290 ['x:y z']
291 ['x:y z']
292 ## END
293
294
295 # TODO:
296 # - unquoted args of whitespace are not elided (when IFS = null)
297 # - empty quoted args are kept
298 # - Test ${@:1} and so forth?
299 #
300 # - $* $@ with empty IFS
301 # - $* $@ with custom IFS
302 #
303 # - no splitting when IFS is empty
304 # - word splitting removes leading and trailing whitespace
305
306 # TODO: test framework needs common setup
307
308 # Test IFS and $@ $* on all these
309 #### TODO
310 empty=""
311 space=" "
312 AB="A B"
313 X="X"
314 Yspaces=" Y "
315
316