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