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