1 # Test extended glob matching with [[, case, etc.
2
3 #### @ matches exactly one
4 shopt -s extglob # needed for Oil, not bash
5 [[ --verbose == --@(help|verbose) ]] && echo TRUE
6 [[ --oops == --@(help|verbose) ]] || echo FALSE
7 ## STDOUT:
8 TRUE
9 FALSE
10 ## END
11
12 #### @() with variable arms
13 shopt -s extglob # needed for Oil, not bash
14 choice1='help'
15 choice2='verbose'
16 [[ --verbose == --@($choice1|$choice2) ]] && echo TRUE
17 [[ --oops == --@($choice1|$choice2) ]] || echo FALSE
18 ## STDOUT:
19 TRUE
20 FALSE
21 ## END
22
23 #### extglob in variable
24 shopt -s extglob
25
26 # this syntax requires extglob in bash!!
27 # OSH never allows it
28 g=--@(help|verbose)
29
30 quoted='--@(help|verbose)'
31
32 [[ --help == $g ]] && echo TRUE
33 [[ --verbose == $g ]] && echo TRUE
34 [[ -- == $g ]] || echo FALSE
35 [[ --help == $q ]] || echo FALSE
36 [[ -- == $q ]] || echo FALSE
37 ## STDOUT:
38 TRUE
39 TRUE
40 FALSE
41 FALSE
42 FALSE
43 ## END
44 ## N-I mksh STDOUT:
45 FALSE
46 FALSE
47 FALSE
48 ## END
49 ## OK osh status: 1
50 ## OK osh STDOUT:
51 ## END
52
53 #### Matching literal '@(cc)'
54
55 # extglob is OFF. Doesn't affect bash or mksh!
56 [[ cc == @(cc) ]]
57 echo status=$?
58 [[ cc == '@(cc)' ]]
59 echo status=$?
60
61 shopt -s extglob
62
63 [[ cc == @(cc) ]]
64 echo status=$?
65 [[ cc == '@(cc)' ]]
66 echo status=$?
67
68 ## STDOUT:
69 status=0
70 status=1
71 status=0
72 status=1
73 ## END
74
75 #### nested @()
76 shopt -s extglob
77 pat='--@(help|verbose|no-@(long|short)-option)'
78 [[ --no-long-option == $pat ]] && echo TRUE
79 [[ --no-short-option == $pat ]] && echo TRUE
80 [[ --help == $pat ]] && echo TRUE
81 [[ --oops == $pat ]] || echo FALSE
82 ## STDOUT:
83 TRUE
84 TRUE
85 TRUE
86 FALSE
87 ## END
88 ## BUG mksh STDOUT:
89 FALSE
90 ## END
91
92 #### nested @() with quotes and vars
93 shopt -s extglob
94 prefix=no
95 [[ --no-long-option == --@(help|verbose|$prefix-@(long|short)-'option') ]] &&
96 echo TRUE
97 ## STDOUT:
98 TRUE
99 ## END
100
101 #### ? matches 0 or 1
102 shopt -s extglob # needed for Oil, not bash
103 [[ -- == --?(help|verbose) ]] && echo TRUE
104 [[ --oops == --?(help|verbose) ]] || echo FALSE
105 ## STDOUT:
106 TRUE
107 FALSE
108 ## END
109
110 #### + matches 1 or more
111 shopt -s extglob # needed for Oil, not bash
112 [[ --helphelp == --+(help|verbose) ]] && echo TRUE
113 [[ -- == --+(help|verbose) ]] || echo FALSE
114 ## STDOUT:
115 TRUE
116 FALSE
117 ## END
118
119 #### * matches 0 or more
120 shopt -s extglob # needed for Oil, not bash
121 [[ -- == --*(help|verbose) ]] && echo TRUE
122 [[ --oops == --*(help|verbose) ]] || echo FALSE
123 ## STDOUT:
124 TRUE
125 FALSE
126 ## END
127
128 #### simple repetition with *(foo) and +(Foo)
129 shopt -s extglob # needed for Oil, not bash
130 [[ foofoo == *(foo) ]] && echo TRUE
131 [[ foofoo == +(foo) ]] && echo TRUE
132 ## STDOUT:
133 TRUE
134 TRUE
135 ## END
136
137 #### ! matches none
138 shopt -s extglob # needed for Oil, not bash
139 [[ --oops == --!(help|verbose) ]] && echo TRUE
140 [[ --help == --!(help|verbose) ]] || echo FALSE
141 ## STDOUT:
142 TRUE
143 FALSE
144 ## END
145
146 #### match is anchored
147 shopt -s extglob # needed for Oil, not bash
148 [[ foo_ == @(foo) ]] || echo FALSE
149 [[ _foo == @(foo) ]] || echo FALSE
150 [[ foo == @(foo) ]] && echo TRUE
151 ## STDOUT:
152 FALSE
153 FALSE
154 TRUE
155 ## END
156
157 #### repeated match is anchored
158 shopt -s extglob # needed for Oil, not bash
159 [[ foofoo_ == +(foo) ]] || echo FALSE
160 [[ _foofoo == +(foo) ]] || echo FALSE
161 [[ foofoo == +(foo) ]] && echo TRUE
162 ## STDOUT:
163 FALSE
164 FALSE
165 TRUE
166 ## END
167
168 #### repetition with glob
169 shopt -s extglob # needed for Oil, not bash
170
171 # NOTE that * means two different things here
172 [[ foofoo_foo__foo___ == *(foo*) ]] && echo TRUE
173 [[ Xoofoo_foo__foo___ == *(foo*) ]] || echo FALSE
174 ## STDOUT:
175 TRUE
176 FALSE
177 ## END
178
179 #### No brace expansion in ==
180 shopt -s extglob # needed for Oil, not bash
181
182 [[ --X{a,b}X == --@(help|X{a,b}X) ]] && echo TRUE
183 [[ --oops == --@(help|X{a,b}X) ]] || echo FALSE
184 ## STDOUT:
185 TRUE
186 FALSE
187 ## END
188
189 #### adjacent extglob
190 shopt -s extglob # needed for Oil, not bash
191
192 [[ --help == @(--|++)@(help|verbose) ]] && echo TRUE
193 [[ ++verbose == @(--|++)@(help|verbose) ]] && echo TRUE
194 ## STDOUT:
195 TRUE
196 TRUE
197 ## END
198
199 #### nested extglob
200 shopt -s extglob # needed for Oil, not bash
201
202 [[ --help == --@(help|verbose=@(1|2)) ]] && echo TRUE
203 [[ --verbose=1 == --@(help|verbose=@(1|2)) ]] && echo TRUE
204 [[ --verbose=2 == --@(help|verbose=@(1|2)) ]] && echo TRUE
205 [[ --verbose == --@(help|verbose=@(1|2)) ]] || echo FALSE
206 ## STDOUT:
207 TRUE
208 TRUE
209 TRUE
210 FALSE
211 ## END
212
213 #### extglob empty string
214 shopt -s extglob
215 [[ '' == @(foo|bar) ]] || echo FALSE
216 [[ '' == @(foo||bar) ]] && echo TRUE
217 ## STDOUT:
218 FALSE
219 TRUE
220 ## END
221
222 #### extglob empty pattern
223 shopt -s extglob
224 [[ '' == @() ]] && echo TRUE
225 [[ '' == @(||) ]] && echo TRUE
226 [[ X == @() ]] || echo FALSE
227 [[ '|' == @(||) ]] || echo FALSE
228 ## STDOUT:
229 TRUE
230 TRUE
231 FALSE
232 FALSE
233 ## END
234
235 #### case with extglob
236 shopt -s extglob
237 for word in --help --verbose --unmatched -- -zxzx -; do
238 case $word in
239 --@(help|verbose) )
240 echo A
241 continue
242 ;;
243 ( --?(b|c) )
244 echo B
245 continue
246 ;;
247 ( -+(x|z) )
248 echo C
249 continue
250 ;;
251 ( -*(x|z) )
252 echo D
253 continue
254 ;;
255 *)
256 echo U
257 continue
258 ;;
259 esac
260 done
261 ## STDOUT:
262 A
263 A
264 U
265 B
266 C
267 D
268 ## END
269
270 #### [[ $x == !($str) ]]
271 shopt -s extglob
272 empty=''
273 str='x'
274 [[ $empty == !($str) ]] && echo TRUE # test glob match
275 [[ $str == !($str) ]] || echo FALSE
276 ## STDOUT:
277 TRUE
278 FALSE
279 ## END
280
281 #### Turning extglob on changes the meaning of [[ !(str) ]] in bash
282 empty=''
283 str='x'
284 [[ !($empty) ]] && echo TRUE # test if $empty is empty
285 [[ !($str) ]] || echo FALSE # test if $str is empty
286 shopt -s extglob # mksh doesn't have this
287 [[ !($empty) ]] && echo TRUE # negated glob
288 [[ !($str) ]] && echo TRUE # negated glob
289 ## STDOUT:
290 TRUE
291 FALSE
292 TRUE
293 TRUE
294 ## END
295 ## OK mksh STDOUT:
296 TRUE
297 TRUE
298 TRUE
299 ## END
300
301 # osh fails wth runtime error
302 ## OK osh status: 1
303 ## OK osh STDOUT:
304 ## END
305
306 #### With extglob on, !($str) on the left or right of == has different meanings
307 shopt -s extglob
308 str='x'
309 [[ 1 == !($str) ]] && echo TRUE # glob match
310
311 ## STDOUT:
312 TRUE
313 ## END
314
315 #### extglob inside arg word
316 shopt -s extglob
317 [[ foo == @(foo|bar) ]] && echo TRUE
318 [[ foo == ${unset:-@(foo|bar)} ]] && echo TRUE
319 [[ fo == ${unset:-@(foo|bar)} ]] || echo FALSE
320 ## STDOUT:
321 TRUE
322 TRUE
323 FALSE
324 ## END
325 ## BUG mksh STDOUT:
326 TRUE
327 FALSE
328 ## END
329 ## OK osh status: 1
330 ## OK osh STDOUT:
331 TRUE
332 ## END
333
334 #### extglob is not detected in regex!
335 shopt -s extglob
336 [[ foo =~ ^@(foo|bar)$ ]] || echo FALSE
337 ## STDOUT:
338 FALSE
339 ## END
340 ## N-I mksh stdout-json: ""
341 ## N-I mksh status: 1
342
343
344 #### regular glob of single unicode char
345 shopt -s extglob
346 [[ __a__ == __?__ ]]
347 echo $?
348 [[ __μ__ == __?__ ]]
349 echo $?
350 ## STDOUT:
351 0
352 0
353 ## END
354 ## BUG mksh STDOUT:
355 0
356 1
357 ## END
358
359 #### extended glob of single unicode char
360 shopt -s extglob
361 [[ __a__ == @(__?__) ]]
362 echo $?
363 [[ __μ__ == @(__?__) ]]
364 echo $?
365 ## STDOUT:
366 0
367 0
368 ## END
369 ## BUG mksh STDOUT:
370 0
371 1
372 ## END
373
374 #### Extended glob in ${x//pat/replace}
375 # not supported in OSH due to GlobToERE() strategy for positional info
376
377 shopt -s extglob
378 x=foo.py
379 echo ${x//@(?.py)/Z}
380 ## STDOUT:
381 foZ
382 ## END
383 ## N-I osh status: 1
384 ## N-I osh stdout-json: ""
385
386 #### Extended glob in ${x%PATTERN}
387
388 shopt -s extglob
389 x=foo.py
390 echo 'strip % ' ${x%.@(py|cc)}
391 echo 'strip %%' ${x%%.@(py|cc)}
392 echo 'strip # ' ${x#@(foo)}
393 echo 'strip ##' ${x##@(foo)}
394
395 ## STDOUT:
396 strip % foo
397 strip %% foo
398 strip # .py
399 strip ## .py
400 ## END