1 ## compare_shells: bash mksh
2 ## oils_failures_allowed: 3
3
4 # TODO: can also run against ksh, brush, sush
5
6 # Test extended glob matching with [[, case, etc.
7
8 #### @ matches exactly one
9 shopt -s extglob # needed for Oil, not bash
10 [[ --verbose == --@(help|verbose) ]] && echo TRUE
11 [[ --oops == --@(help|verbose) ]] || echo FALSE
12 ## STDOUT:
13 TRUE
14 FALSE
15 ## END
16
17 #### @() with variable arms
18 shopt -s extglob # needed for Oil, not bash
19 choice1='help'
20 choice2='verbose'
21 [[ --verbose == --@($choice1|$choice2) ]] && echo TRUE
22 [[ --oops == --@($choice1|$choice2) ]] || echo FALSE
23 ## STDOUT:
24 TRUE
25 FALSE
26 ## END
27
28 #### extglob in variable
29 shopt -s extglob
30
31 # this syntax requires extglob in bash!!
32 # OSH never allows it
33 g=--@(help|verbose)
34
35 quoted='--@(help|verbose)'
36
37 [[ --help == $g ]] && echo TRUE
38 [[ --verbose == $g ]] && echo TRUE
39 [[ -- == $g ]] || echo FALSE
40 [[ --help == $q ]] || echo FALSE
41 [[ -- == $q ]] || echo FALSE
42 ## STDOUT:
43 TRUE
44 TRUE
45 FALSE
46 FALSE
47 FALSE
48 ## END
49 ## N-I mksh STDOUT:
50 FALSE
51 FALSE
52 FALSE
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 ## N-I mksh/ksh STDOUT:
298 TRUE
299 TRUE
300 TRUE
301 ## END
302
303 #### With extglob on, !($str) on the left or right of == has different meanings
304 shopt -s extglob
305 str='x'
306 [[ 1 == !($str) ]] && echo TRUE # glob match
307
308 ## STDOUT:
309 TRUE
310 ## END
311
312 #### extglob inside arg word
313 shopt -s extglob
314 [[ foo == @(foo|bar) ]] && echo rhs
315 [[ foo == ${unset:-@(foo|bar)} ]] && echo 'rhs arg'
316 [[ fo == ${unset:-@(foo|bar)} ]] || echo nope
317 ## STDOUT:
318 rhs
319 rhs arg
320 nope
321 ## END
322 ## BUG mksh/ksh STDOUT:
323 rhs
324 nope
325 ## END
326
327 #### extglob is not detected in regex!
328 shopt -s extglob
329 [[ foo =~ ^@(foo|bar)$ ]] || echo FALSE
330 ## STDOUT:
331 FALSE
332 ## END
333 ## N-I mksh/ksh stdout-json: ""
334 ## N-I mksh/ksh status: 1
335
336 #### regular glob of single unicode char
337 shopt -s extglob
338 [[ __a__ == __?__ ]]
339 echo $?
340 [[ __μ__ == __?__ ]]
341 echo $?
342 ## STDOUT:
343 0
344 0
345 ## END
346 ## BUG mksh STDOUT:
347 0
348 1
349 ## END
350
351 #### extended glob of single unicode char
352 shopt -s extglob
353 [[ __a__ == @(__?__) ]]
354 echo $?
355 [[ __μ__ == @(__?__) ]]
356 echo $?
357 ## STDOUT:
358 0
359 0
360 ## END
361 ## BUG mksh STDOUT:
362 0
363 1
364 ## END
365
366 #### Extended glob in ${x//pat/replace}
367 # not supported in OSH due to GlobToERE() strategy for positional info
368
369 shopt -s extglob
370 x=foo.py
371 echo ${x//@(?.py)/Z}
372 ## STDOUT:
373 foZ
374 ## END
375 ## N-I osh status: 1
376 ## N-I osh stdout-json: ""
377
378 #### Extended glob in ${x%PATTERN}
379
380 shopt -s extglob
381 x=foo.py
382 echo 'strip % ' ${x%.@(py|cc)}
383 echo 'strip %%' ${x%%.@(py|cc)}
384 echo 'strip # ' ${x#@(foo)}
385 echo 'strip ##' ${x##@(foo)}
386
387 ## STDOUT:
388 strip % foo
389 strip %% foo
390 strip # .py
391 strip ## .py
392 ## END