1 # Test ${x/pat*/replace}
2 # TODO: can add $BUSYBOX_ASH
3
4 ## oils_failures_allowed: 2
5 ## compare_shells: bash mksh zsh
6
7 #### Pattern replacement
8 v=abcde
9 echo ${v/c*/XX}
10 ## stdout: abXX
11
12 #### Pattern replacement on unset variable
13 echo -${v/x/y}-
14 echo status=$?
15 set -o nounset # make sure this fails
16 echo -${v/x/y}-
17 ## STDOUT:
18 --
19 status=0
20 ## BUG mksh STDOUT:
21 # patsub disrespects nounset!
22 --
23 status=0
24 --
25 ## status: 1
26 ## OK ash status: 2
27 ## BUG mksh status: 0
28
29 #### Global Pattern replacement with /
30 s=xx_xx_xx
31 echo ${s/xx?/yy_} ${s//xx?/yy_}
32 ## stdout: yy_xx_xx yy_yy_xx
33
34 #### Left Anchored Pattern replacement with #
35 s=xx_xx_xx
36 echo ${s/?xx/_yy} ${s/#?xx/_yy}
37 ## stdout: xx_yy_xx xx_xx_xx
38
39 #### Right Anchored Pattern replacement with %
40 s=xx_xx_xx
41 echo ${s/?xx/_yy} ${s/%?xx/_yy}
42 ## STDOUT:
43 xx_yy_xx xx_xx_yy
44 ## END
45 ## BUG ash STDOUT:
46 xx_yy_xx xx_xx_xx
47 ## END
48
49 #### Replace fixed strings
50 s=xx_xx
51 echo ${s/xx/yy} ${s//xx/yy} ${s/#xx/yy} ${s/%xx/yy}
52 ## STDOUT:
53 yy_xx yy_yy yy_xx xx_yy
54 ## END
55 ## BUG ash STDOUT:
56 yy_xx yy_yy xx_xx xx_xx
57 ## END
58
59 #### Replace is longest match
60 # If it were shortest, then you would just replace the first <html>
61 s='begin <html></html> end'
62 echo ${s/<*>/[]}
63 ## stdout: begin [] end
64
65 #### Replace char class
66 s=xx_xx_xx
67 echo ${s//[[:alpha:]]/y} ${s//[^[:alpha:]]/-}
68 ## stdout: yy_yy_yy xx-xx-xx
69 ## N-I mksh stdout: xx_xx_xx xx_xx_xx
70
71 #### Replace hard glob
72 s='aa*bb+cc'
73 echo ${s//\**+/__} # Literal *, then any sequence of characters, then literal +
74 ## stdout: aa__cc
75
76 #### ${v/} is empty search and replacement
77 v=abcde
78 echo -${v/}-
79 echo status=$?
80 ## status: 0
81 ## STDOUT:
82 -abcde-
83 status=0
84 ## END
85 ## BUG ash STDOUT:
86 -abcde -
87 status=0
88 ## END
89
90 #### ${v//} is empty search and replacement
91 v='a/b/c'
92 echo -${v//}-
93 echo status=$?
94 ## status: 0
95 ## STDOUT:
96 -a/b/c-
97 status=0
98 ## END
99 ## BUG ash STDOUT:
100 -a/b/c -
101 status=0
102 ## END
103
104 #### Confusing unquoted slash matches bash (and ash)
105 x='/_/'
106 echo ${x////c}
107
108 echo ${x//'/'/c}
109
110 ## STDOUT:
111 c_c
112 c_c
113 ## END
114 ## BUG mksh STDOUT:
115 /_/
116 c_c
117 ## END
118 ## BUG zsh STDOUT:
119 /c//c_/c/
120 /_/
121 ## END
122 ## BUG ash STDOUT:
123 c_c
124 /_/ /c
125 ## END
126
127 #### Synthesized ${x///} bug (similar to above)
128
129 # found via test/parse-errors.sh
130
131 x='slash / brace } hi'
132 echo 'ambiguous:' ${x///}
133
134 echo 'quoted: ' ${x//'/'}
135
136 # Wow we have all combination here -- TERRIBLE
137
138 ## STDOUT:
139 ambiguous: slash brace } hi
140 quoted: slash brace } hi
141 ## END
142 ## BUG mksh STDOUT:
143 ambiguous: slash / brace } hi
144 quoted: slash brace } hi
145 ## END
146 ## BUG zsh STDOUT:
147 ambiguous: slash / brace } hi
148 quoted: slash / brace } hi
149 ## END
150 ## BUG ash STDOUT:
151 ambiguous: slash brace } hi
152 quoted: slash / brace } hi
153 ## END
154
155
156 #### ${v/a} is the same as ${v/a/} -- no replacement string
157 v='aabb'
158 echo ${v/a}
159 echo status=$?
160 ## STDOUT:
161 abb
162 status=0
163 ## END
164
165 #### Replacement with special chars (bug fix)
166 v=xx
167 echo ${v/x/"?"}
168 ## stdout: ?x
169
170 #### Replace backslash
171 v='[\f]'
172 x='\f'
173 echo ${v/"$x"/_}
174
175 # mksh and zsh differ on this case, but this is consistent with the fact that
176 # \f as a glob means 'f', not '\f'. TODO: Warn that it's a bad glob?
177 # The canonical form is 'f'.
178 echo ${v/$x/_}
179
180 echo ${v/\f/_}
181 echo ${v/\\f/_}
182 ## STDOUT:
183 [_]
184 [\_]
185 [\_]
186 [_]
187 ## END
188 ## BUG mksh/zsh STDOUT:
189 [_]
190 [_]
191 [\_]
192 [_]
193 ## END
194
195 #### Replace right ]
196 v='--]--'
197 x=']'
198 echo ${v/"$x"/_}
199 echo ${v/$x/_}
200 ## STDOUT:
201 --_--
202 --_--
203 ## END
204
205 #### Substitute glob characters in pattern, quoted and unquoted
206
207 # INFINITE LOOP in ash!
208 case $SH in ash) exit ;; esac
209
210 g='*'
211 v='a*b'
212 echo ${v//"$g"/-}
213 echo ${v//$g/-}
214 ## STDOUT:
215 a-b
216 -
217 ## END
218 ## BUG zsh STDOUT:
219 a-b
220 a-b
221 ## END
222
223 #### Substitute one unicode character (UTF-8)
224 export LANG='en_US.UTF-8'
225
226 s='_μ_ and _μ_'
227
228 # ? should match one char
229
230 echo ${s//_?_/foo} # all
231 echo ${s/#_?_/foo} # left
232 echo ${s/%_?_/foo} # right
233
234 ## STDOUT:
235 foo and foo
236 foo and _μ_
237 _μ_ and foo
238 ## END
239 ## BUG mksh STDOUT:
240 _μ_ and _μ_
241 _μ_ and _μ_
242 _μ_ and _μ_
243 ## END
244
245 #### Can't substitute one unicode character when LC_ALL=C
246 export LC_ALL='C'
247
248 s='_μ_ and _μ_'
249
250 # ? should match one char
251
252 echo ${s//_?_/foo} # all
253 echo ${s/#_?_/foo} # left
254 echo ${s/%_?_/foo} # right
255
256 ## STDOUT:
257 _μ_ and _μ_
258 _μ_ and _μ_
259 _μ_ and _μ_
260 ## END
261
262 #### ${x/^} regression
263 x=abc
264 echo ${x/^}
265 echo ${x/!}
266
267 y=^^^
268 echo ${y/^}
269 echo ${y/!}
270
271 z=!!!
272 echo ${z/^}
273 echo ${z/!}
274
275 s=a^b!c
276 echo ${s/a^}
277 echo ${s/b!}
278
279 ## STDOUT:
280 abc
281 abc
282 ^^
283 ^^^
284 !!!
285 !!
286 b!c
287 a^c
288 ## END
289
290 #### \(\) in pattern (regression)
291
292 # Not extended globs
293 x='foo()'
294 echo 1 ${x//*\(\)/z}
295 echo 2 ${x//*\(\)/z}
296 echo 3 ${x//\(\)/z}
297 echo 4 ${x//*\(\)/z}
298
299 ## STDOUT:
300 1 z
301 2 z
302 3 fooz
303 4 z
304 ## END
305
306 #### patsub with single quotes and hyphen in character class (regression)
307
308 # from Crestwave's bf.bash
309
310 program='^++--hello.,world<>[]'
311 program=${program//[^'><+-.,[]']}
312 echo $program
313 ## STDOUT:
314 ++--.,<>[]
315 ## END
316 ## BUG mksh STDOUT:
317 helloworld
318 ## END
319
320 #### patsub with [^]]
321
322 # This is a PARSING divergence. In Oil we match [], rather than using POSIX
323 # rules!
324
325 pat='[^]]'
326 s='ab^cd^'
327 echo ${s//$pat/z}
328 ## STDOUT:
329 ab^cd^
330 ## END
331
332 #### [a-z] Invalid range end is syntax error
333 x=fooz
334 pat='[z-a]' # Invalid range. Other shells don't catch it!
335 #pat='[a-y]'
336 echo ${x//$pat}
337 echo status=$?
338 ## stdout-json: ""
339 ## status: 1
340 ## OK bash/mksh/zsh/ash STDOUT:
341 fooz
342 status=0
343 ## END
344 ## OK bash/mksh/zsh/ash status: 0
345
346
347 #### Pattern is empty $foo$bar -- regression for infinite loop
348
349 x=-foo-
350
351 echo ${x//$foo$bar/bar}
352
353 ## STDOUT:
354 -foo-
355 ## END
356
357 # feels like memory unsafety in ZSH
358 ## BUG zsh STDOUT:
359 bar-barfbarobarobar-
360 ## END
361
362 #### Chromium from http://www.oilshell.org/blog/2016/11/07.html
363
364 case $SH in zsh) exit ;; esac
365
366 HOST_PATH=/foo/bar/baz
367 echo ${HOST_PATH////\\/}
368
369 # The way bash parses it
370 echo ${HOST_PATH//'/'/\\/}
371
372 ## STDOUT:
373 \/foo\/bar\/baz
374 \/foo\/bar\/baz
375 ## END
376
377 # zsh has crazy bugs
378 ## BUG zsh stdout-json: ""
379
380 ## BUG mksh STDOUT:
381 /foo/bar/baz
382 \/foo\/bar\/baz
383 ## END
384
385
386 #### ${x//~homedir/}
387
388 path=~/git/oilshell
389
390 # ~ expansion occurs
391 #echo path=$path
392
393 echo ${path//~/z}
394
395 echo ${path/~/z}
396
397 ## STDOUT:
398 z/git/oilshell
399 z/git/oilshell
400 ## END
401
402