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 with 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
|