1 #!/bin/bash
2 #
3 # Test ${x/pat*/replace}
4
5 #### Pattern replacement
6 v=abcde
7 echo ${v/c*/XX}
8 ## stdout: abXX
9
10 #### Pattern replacement on unset variable
11 echo -${v/x/y}-
12 echo status=$?
13 set -o nounset # make sure this fails
14 echo -${v/x/y}-
15 ## STDOUT:
16 --
17 status=0
18 ## BUG mksh STDOUT:
19 # patsub disrespects nounset!
20 --
21 status=0
22 --
23 ## status: 1
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: xx_yy_xx xx_xx_yy
40
41 #### Replace fixed strings
42 s=xx_xx
43 echo ${s/xx/yy} ${s//xx/yy} ${s/#xx/yy} ${s/%xx/yy}
44 ## stdout: yy_xx yy_yy yy_xx xx_yy
45
46 #### Replace is longest match
47 # If it were shortest, then you would just replace the first <html>
48 s='begin <html></html> end'
49 echo ${s/<*>/[]}
50 ## stdout: begin [] end
51
52 #### Replace char class
53 s=xx_xx_xx
54 echo ${s//[[:alpha:]]/y} ${s//[^[:alpha:]]/-}
55 ## stdout: yy_yy_yy xx-xx-xx
56 ## N-I mksh stdout: xx_xx_xx xx_xx_xx
57
58 #### Replace hard glob
59 s='aa*bb+cc'
60 echo ${s//\**+/__} # Literal *, then any sequence of characters, then literal +
61 ## stdout: aa__cc
62
63 #### Pattern replacement ${v/} is not valid
64 v=abcde
65 echo -${v/}-
66 echo status=$?
67 ## status: 2
68 ## stdout-json: ""
69 ## BUG bash/mksh/zsh status: 0
70 ## BUG bash/mksh/zsh STDOUT:
71 -abcde-
72 status=0
73 ## END
74
75 #### Pattern replacement ${v//} is not valid
76 v='a/b/c'
77 echo -${v//}-
78 echo status=$?
79 ## status: 2
80 ## stdout-json: ""
81 ## BUG bash/mksh/zsh status: 0
82 ## BUG bash/mksh/zsh STDOUT:
83 -a/b/c-
84 status=0
85 ## END
86
87 #### Confusing unquoted slash matches bash (and ash)
88 x='/_/'
89 echo ${x////c}
90 echo ${x//'/'/c}
91 ## STDOUT:
92 c_c
93 c_c
94 ## END
95 ## BUG mksh/yash STDOUT:
96 /_/
97 c_c
98 ## END
99 ## BUG zsh STDOUT:
100 /c//c_/c/
101 /_/
102 ## END
103 ## BUG ash STDOUT:
104 c_c
105 /_/
106 ## END
107
108 #### ${v/a} is the same as ${v/a/} -- no replacement string
109 v='aabb'
110 echo ${v/a}
111 echo status=$?
112 ## STDOUT:
113 abb
114 status=0
115 ## END
116
117 #### Replacement with special chars (bug fix)
118 v=xx
119 echo ${v/x/"?"}
120 ## stdout: ?x
121
122 #### Replace backslash
123 v='[\f]'
124 x='\f'
125 echo ${v/"$x"/_}
126
127 # mksh and zsh differ on this case, but this is consistent with the fact that
128 # \f as a glob means 'f', not '\f'. TODO: Warn that it's a bad glob?
129 # The canonical form is 'f'.
130 echo ${v/$x/_}
131
132 echo ${v/\f/_}
133 echo ${v/\\f/_}
134 ## STDOUT:
135 [_]
136 [\_]
137 [\_]
138 [_]
139 ## END
140 ## BUG mksh/zsh STDOUT:
141 [_]
142 [_]
143 [\_]
144 [_]
145 ## END
146
147 #### Replace right ]
148 v='--]--'
149 x=']'
150 echo ${v/"$x"/_}
151 echo ${v/$x/_}
152 ## STDOUT:
153 --_--
154 --_--
155 ## END
156
157 #### Substitute glob characters in pattern, quoted and unquoted
158 g='*'
159 v='a*b'
160 echo ${v//"$g"/-}
161 echo ${v//$g/-}
162 ## STDOUT:
163 a-b
164 -
165 ## END
166 ## BUG zsh STDOUT:
167 a-b
168 a-b
169 ## END
170
171 #### Substitute one unicode character (UTF-8)
172 export LANG='en_US.UTF-8'
173
174 s='_μ_ and _μ_'
175
176 # ? should match one char
177
178 echo ${s//_?_/foo} # all
179 echo ${s/#_?_/foo} # left
180 echo ${s/%_?_/foo} # right
181
182 ## STDOUT:
183 foo and foo
184 foo and _μ_
185 _μ_ and foo
186 ## END
187 ## BUG mksh STDOUT:
188 _μ_ and _μ_
189 _μ_ and _μ_
190 _μ_ and _μ_
191 ## END
192
193 #### Can't substitute one unicode character when LANG=C
194 export LANG='C'
195 export LC_CTYPE='C'
196
197 s='_μ_ and _μ_'
198
199 # ? should match one char
200
201 echo ${s//_?_/foo} # all
202 echo ${s/#_?_/foo} # left
203 echo ${s/%_?_/foo} # right
204
205 ## STDOUT:
206 _μ_ and _μ_
207 _μ_ and _μ_
208 _μ_ and _μ_
209 ## END
210
211 #### ${x/^} regression
212 x=abc
213 echo ${x/^}
214 echo ${x/!}
215
216 y=^^^
217 echo ${y/^}
218 echo ${y/!}
219
220 z=!!!
221 echo ${z/^}
222 echo ${z/!}
223
224 s=a^b!c
225 echo ${s/a^}
226 echo ${s/b!}
227
228 ## STDOUT:
229 abc
230 abc
231 ^^
232 ^^^
233 !!!
234 !!
235 b!c
236 a^c
237 ## END
238
239 #### \(\) in pattern (regression)
240
241 # Not extended globs
242 x='foo()'
243 echo 1 ${x//*\(\)/z}
244 echo 2 ${x//*\(\)/z}
245 echo 3 ${x//\(\)/z}
246 echo 4 ${x//*\(\)/z}
247
248 ## STDOUT:
249 1 z
250 2 z
251 3 fooz
252 4 z
253 ## END
254
255
256 #### Extended globs! (not supported in Oil)
257 shopt -s extglob
258
259 x='foo()'
260 echo ext ${x//*(foo|bar)/z}
261 echo ext "${x//*(foo|bar)/z}"
262
263 ## STDOUT:
264 ext z()
265 ext z()
266 ## END
267
268 # I don't get what bash is doing here!
269 ## BUG bash STDOUT:
270 ext zz(z)
271 ext zz(z)
272 ## END
273
274 # GlobToERE doesn't support extended globs!
275
276 ## N-I osh STDOUT:
277 ext foo()
278 ext foo()
279 ## END
280
281 #### patsub with single quotes and hyphen in character class (regression)
282
283 # from Crestwave's bf.bash
284
285 program='^++--hello.,world<>[]'
286 program=${program//[^'><+-.,[]']}
287 echo $program
288 ## STDOUT:
289 ++--.,<>[]
290 ## END
291 ## BUG mksh STDOUT:
292 helloworld
293 ## END
294
295 #### patsub with [^]]
296
297 # This is a PARSING divergence. In Oil we match [], rather than using POSIX
298 # rules!
299
300 pat='[^]]'
301 s='ab^cd^'
302 echo ${s//$pat/z}
303 ## STDOUT:
304 ab^cd^
305 ## END
306
307 #### patsub syntax error
308 x=fooz
309 pat='[z-a]' # Invalid range. Other shells don't catch it!
310 #pat='[a-y]'
311 echo ${x//$pat}
312 echo status=$?
313 ## stdout-json: ""
314 ## status: 1
315 ## OK bash/mksh/zsh STDOUT:
316 fooz
317 status=0
318 ## END
319 ## OK bash/mksh/zsh status: 0