1 ## oils_failures_allowed: 3
2 ## compare_shells: bash mksh
3
4 # NOTE: zsh passes about half, and fails about half. It supports a subset of
5 # [[ I guess.
6
7 #### [[ glob matching, [[ has no glob expansion
8 [[ foo.py == *.py ]] && echo true
9 [[ foo.p == *.py ]] || echo false
10 ## stdout-json: "true\nfalse\n"
11
12 #### [[ glob matching with escapes
13 [[ 'foo.*' == *."*" ]] && echo true
14 # note that the pattern arg to fnmatch should be '*.\*'
15 ## stdout: true
16
17 #### equality
18 [[ '*.py' == '*.py' ]] && echo true
19 [[ foo.py == '*.py' ]] || echo false
20 ## stdout-json: "true\nfalse\n"
21
22 #### [[ glob matching with unquoted var
23 pat=*.py
24 [[ foo.py == $pat ]] && echo true
25 [[ foo.p == $pat ]] || echo false
26 ## stdout-json: "true\nfalse\n"
27
28 #### [[ regex matching
29 # mksh doesn't have this syntax of regex matching. I guess it comes from perl?
30 regex='.*\.py'
31 [[ foo.py =~ $regex ]] && echo true
32 [[ foo.p =~ $regex ]] || echo false
33 ## stdout-json: "true\nfalse\n"
34 ## N-I mksh stdout-json: ""
35 ## N-I mksh status: 1
36
37 #### [[ regex syntax error
38 # hm, it doesn't show any error, but it exits 2.
39 [[ foo.py =~ * ]] && echo true
40 ## status: 2
41 ## N-I mksh status: 1
42
43 #### [[ has no word splitting
44 var='one two'
45 [[ 'one two' == $var ]] && echo true
46 ## stdout: true
47
48 #### [[ has quote joining
49 var='one two'
50 [[ 'one 'tw"o" == $var ]] && echo true
51 ## stdout: true
52
53 #### [[ empty string is false
54 [[ 'a' ]] && echo true
55 [[ '' ]] || echo false
56 ## stdout-json: "true\nfalse\n"
57
58 #### && chain
59 [[ t && t && '' ]] || echo false
60 ## stdout: false
61
62 #### || chain
63 [[ '' || '' || t ]] && echo true
64 ## stdout: true
65
66 #### [[ compound expressions
67 # Notes on whitespace:
68 # - 1 and == need space seprating them, but ! and ( don't.
69 # - [[ needs whitesapce after it, but ]] doesn't need whitespace before it!
70 [[ ''||! (1 == 2)&&(2 == 2)]] && echo true
71 ## stdout: true
72
73 # NOTE on the two cases below. We're comparing
74 # (a || b) && c vs. a || (b && c)
75 #
76 # a = true, b = false, c = false is an example where they are different.
77 # && and || have precedence inside
78
79 #### precedence of && and || inside [[
80 [[ True || '' && '' ]] && echo true
81 ## stdout: true
82
83 #### precedence of && and || in a command context
84 if test True || test '' && test ''; then
85 echo YES
86 else
87 echo "NO precedence"
88 fi
89 ## stdout: NO precedence
90
91 # http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
92
93 #### Octal literals with -eq
94 shopt -u strict_arith || true
95 decimal=15
96 octal=017 # = 15 (decimal)
97 [[ $decimal -eq $octal ]] && echo true
98 [[ $decimal -eq ZZZ$octal ]] || echo false
99 ## STDOUT:
100 true
101 false
102 ## END
103 ## N-I mksh stdout: false
104 # mksh doesn't implement this syntax for literals.
105
106 #### Hex literals with -eq
107 shopt -u strict_arith || true
108 decimal=15
109 hex=0x0f # = 15 (decimal)
110 [[ $decimal -eq $hex ]] && echo true
111 [[ $decimal -eq ZZZ$hex ]] || echo false
112 ## stdout-json: "true\nfalse\n"
113 ## N-I mksh stdout: false
114
115 # TODO: Add tests for this
116 # https://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions
117 # When used with [[, the ‘<’ and ‘>’ operators sort lexicographically using the
118 # current locale. The test command uses ASCII ordering.
119
120 #### > on strings
121 # NOTE: < doesn't need space, even though == does? That's silly.
122 [[ b>a ]] && echo true
123 [[ b<a ]] || echo false
124 ## stdout-json: "true\nfalse\n"
125
126 #### != on strings
127 # NOTE: b!=a does NOT work
128 [[ b != a ]] && echo true
129 [[ a != a ]] || echo false
130 ## stdout-json: "true\nfalse\n"
131
132 #### -eq on strings
133 # This is lame behavior: it does a conversion to 0 first for any string
134 shopt -u strict_arith || true
135 [[ a -eq a ]] && echo true
136 [[ a -eq b ]] && echo true
137 ## STDOUT:
138 true
139 true
140 ## END
141
142 #### [[ compare with literal -f (compare with test-builtin.test.sh)
143 var=-f
144 [[ $var == -f ]] && echo true
145 [[ '-f' == $var ]] && echo true
146 ## stdout-json: "true\ntrue\n"
147
148 #### [[ with op variable (compare with test-builtin.test.sh)
149 # Parse error -- parsed BEFORE evaluation of vars
150 op='=='
151 [[ a $op a ]] && echo true
152 [[ a $op b ]] || echo false
153 ## status: 2
154 ## OK mksh status: 1
155
156 #### [[ with unquoted empty var (compare with test-builtin.test.sh)
157 empty=''
158 [[ $empty == '' ]] && echo true
159 ## stdout: true
160
161 #### [[ at runtime doesn't work
162 dbracket=[[
163 $dbracket foo == foo ]]
164 ## status: 127
165
166 #### [[ with env prefix doesn't work
167 FOO=bar [[ foo == foo ]]
168 ## status: 127
169
170 #### [[ over multiple lines is OK
171 # Hm it seems you can't split anywhere?
172 [[ foo == foo
173 && bar == bar
174 ]] && echo true
175 ## status: 0
176 ## STDOUT:
177 true
178 ## END
179
180 #### Argument that looks like a command word operator
181 [[ -f -f ]] || echo false
182 [[ -f == ]] || echo false
183 ## STDOUT:
184 false
185 false
186 ## END
187
188 #### Argument that looks like a real operator
189 [[ -f < ]] && echo 'should be parse error'
190 ## status: 2
191 ## OK mksh status: 1
192
193 #### User array compared to "$@" (broken unless shopt -s strict_array)
194 # Both are coerced to string! It treats it more like an UNQUOTED ${a[@]}.
195
196 a=('1 3' 5)
197 b=(1 2 3)
198 set -- 1 '3 5'
199 [[ "$@" = "${a[@]}" ]] && echo true
200 [[ "$@" = "${b[@]}" ]] || echo false
201 ## STDOUT:
202 true
203 false
204 ## END
205
206 #### Array coerces to string (shopt -s strict_array to disallow)
207 a=('1 3' 5)
208 [[ '1 3 5' = "${a[@]}" ]] && echo true
209 [[ '1 3 4' = "${a[@]}" ]] || echo false
210 ## STDOUT:
211 true
212 false
213 ## END
214
215 #### (( array1 == array2 )) doesn't work
216 a=('1 3' 5)
217 b=('1 3' 5)
218 c=('1' '3 5')
219 d=('1' '3 6')
220
221 # shells EXPAND a and b first
222 (( a == b ))
223 echo status=$?
224
225 (( a == c ))
226 echo status=$?
227
228 (( a == d ))
229 echo status=$?
230
231 ## stdout-json: ""
232 ## status: 1
233 ## BUG bash STDOUT:
234 status=1
235 status=1
236 status=1
237 ## END
238 ## BUG bash status: 0
239
240 #### Quotes don't matter in comparison
241 [[ '3' = 3 ]] && echo true
242 [[ '3' -eq 3 ]] && echo true
243 ## STDOUT:
244 true
245 true
246 ## END
247
248 #### -eq does dynamic arithmetic parsing (not supported in OSH)
249 [[ 1+2 -eq 3 ]] && echo true
250 expr='1+2'
251 [[ $expr -eq 3 ]] && echo true # must be dynamically parsed
252 ## STDOUT:
253 true
254 true
255 ## END
256
257 #### -eq coercion produces weird results
258 shopt -u strict_arith || true
259 [[ '' -eq 0 ]] && echo true
260 ## stdout: true
261
262 #### [[ '(' ]] is treated as literal
263 [[ '(' ]]
264 echo status=$?
265 ## stdout: status=0
266
267 #### [[ '(' foo ]] is syntax error
268 [[ '(' foo ]]
269 echo status=$?
270 ## status: 2
271 ## OK mksh status: 1
272
273 #### empty ! is treated as literal
274 [[ '!' ]]
275 echo status=$?
276 ## stdout: status=0
277
278 #### [[ -z ]] is syntax error
279 [[ -z ]]
280 echo status=$?
281 ## status: 2
282 ## OK mksh status: 1
283
284 #### [[ -z '>' ]]
285 [[ -z '>' ]] || echo false # -z is operator
286 ## stdout: false
287
288 #### [[ -z '>' a ]] is syntax error
289 [[ -z '>' -- ]]
290 echo status=$?
291 ## status: 2
292 ## OK mksh status: 1
293
294 #### test whether ']]' is empty
295 [[ ']]' ]]
296 echo status=$?
297 ## status: 0
298
299 #### [[ ]] is syntax error
300 [[ ]]
301 echo status=$?
302 ## stdout-json: ""
303 ## status: 2
304 ## OK mksh status: 1
305
306 #### [[ && ]] is syntax error
307 [[ && ]]
308 echo status=$?
309 ## stdout-json: ""
310 ## status: 2
311 ## OK mksh status: 1
312
313 #### [[ a 3< b ]] doesn't work (bug regression)
314 [[ a 3< b ]]
315 echo status=$?
316 [[ a 3> b ]]
317 echo status=$?
318 ## status: 2
319
320 # Hm these shells use the same redirect trick that OSH used to!
321
322 ## BUG mksh/zsh status: 0
323 ## BUG mksh/zsh STDOUT:
324 status=0
325 status=1
326 ## END
327
328 #### tilde expansion in [[
329 HOME=/home/bob
330 [[ ~ == /home/bob ]]
331 echo status=$?
332
333 [[ ~ == */bob ]]
334 echo status=$?
335
336 [[ ~ == */z ]]
337 echo status=$?
338
339 ## STDOUT:
340 status=0
341 status=0
342 status=1
343 ## END
344
345 #### more tilde expansion
346 [[ ~ ]]
347 echo status=$?
348 HOME=''
349 [[ ~ ]]
350 echo status=$?
351 [[ -n ~ ]]
352 echo unary=$?
353
354 [[ ~ == ~ ]]
355 echo status=$?
356
357 [[ $HOME == ~ ]]
358 echo fnmatch=$?
359 [[ ~ == $HOME ]]
360 echo fnmatch=$?
361
362 ## STDOUT:
363 status=0
364 status=1
365 unary=1
366 status=0
367 fnmatch=0
368 fnmatch=0
369 ## END
370
371 #### tilde expansion with =~ (confusing)
372 case $SH in (mksh) exit ;; esac
373
374 HOME=foo
375 [[ ~ =~ $HOME ]]
376 echo regex=$?
377 [[ $HOME =~ ~ ]]
378 echo regex=$?
379
380 HOME='^a$' # looks like regex
381 [[ ~ =~ $HOME ]]
382 echo regex=$?
383 [[ $HOME =~ ~ ]]
384 echo regex=$?
385
386 ## STDOUT:
387 regex=0
388 regex=0
389 regex=1
390 regex=0
391 ## END
392 ## OK zsh STDOUT:
393 regex=0
394 regex=0
395 regex=1
396 regex=1
397 ## END
398 ## N-I mksh stdout-json: ""
399
400 #### [[ ]] with redirect
401 [[ $(stdout_stderr.py) == STDOUT ]] 2>$TMP/x.txt
402 echo $?
403 echo --
404 cat $TMP/x.txt
405 ## STDOUT:
406 0
407 --
408 STDERR
409 ## END
410
411 #### special chars
412 [[ ^ == ^ ]]
413 echo caret $?
414 [[ '!' == ! ]]
415 echo bang $?
416 ## STDOUT:
417 caret 0
418 bang 0
419 ## END
420
421
422 #### \(\) in pattern (regression)
423 if [[ 'foo()' == *\(\) ]]; then echo match1; fi
424 if [[ 'foo()' == *'()' ]]; then echo match2; fi
425 if [[ 'foo()' == '*()' ]]; then echo match3; fi
426
427 shopt -s extglob
428
429 if [[ 'foo()' == *\(\) ]]; then echo match1; fi
430 if [[ 'foo()' == *'()' ]]; then echo match2; fi
431 if [[ 'foo()' == '*()' ]]; then echo match3; fi
432
433 ## STDOUT:
434 match1
435 match2
436 match1
437 match2
438 ## END
439
440
441 #### [[ -v array[i] ]]
442
443 typeset -a array
444 array=('' nonempty)
445
446 [[ -v array[0] ]]
447 echo zero=$?
448
449 [[ -v array[1] ]]
450 echo one=$?
451
452 [[ -v array[2] ]]
453 echo two=$?
454
455 ## STDOUT:
456 zero=0
457 one=0
458 two=1
459 ## END
460
461 ## N-I mksh status: 1
462 ## N-I mksh STDOUT:
463 ## END
464
465 #### [[ -v array[expr]] ]] does arith expression evaluation
466
467 typeset -a array
468 array=('' nonempty)
469
470 # This feels inconsistent with the rest of bash?
471 zero=0
472
473 [[ -v array[zero+0] ]]
474 echo zero=$?
475
476 [[ -v array[zero+1] ]]
477 echo one=$?
478
479 [[ -v array[zero+2] ]]
480 echo two=$?
481
482 echo ---
483
484 i='0+0'
485 [[ -v array[i] ]]
486 echo zero=$?
487
488 i='0+1'
489 [[ -v array[i] ]]
490 echo one=$?
491
492 i='0+2'
493 [[ -v array[i] ]]
494 echo two=$?
495
496 echo ---
497
498 i='0+0'
499 [[ -v array[$i] ]]
500 echo zero=$?
501
502 i='0+1'
503 [[ -v array[$i] ]]
504 echo one=$?
505
506 i='0+2'
507 [[ -v array[$i] ]]
508 echo two=$?
509
510
511 ## STDOUT:
512 zero=0
513 one=0
514 two=1
515 ---
516 zero=0
517 one=0
518 two=1
519 ---
520 zero=0
521 one=0
522 two=1
523 ## END
524
525 ## N-I mksh status: 1
526 ## N-I mksh STDOUT:
527 ## END
528
529 #### [[ -v assoc[key] ]]
530
531 typeset -A assoc
532 assoc=([empty]='' [k]=v)
533
534 [[ -v assoc[empty] ]]
535 echo empty=$?
536
537 [[ -v assoc[k] ]]
538 echo k=$?
539
540 [[ -v assoc[nonexistent] ]]
541 echo nonexistent=$?
542
543 echo ---
544 # Now with quotes
545
546 [[ -v assoc["empty"] ]]
547 echo empty=$?
548
549 [[ -v assoc['k'] ]]
550 echo k=$?
551
552 [[ -v assoc['nonexistent'] ]]
553 echo nonexistent=$?
554
555 echo ---
556 # Now with var expansion
557
558 key=empty
559 [[ -v assoc[$key] ]]
560 echo empty=$?
561
562 key=k
563 [[ -v assoc[$key] ]]
564 echo k=$?
565
566 key=nonexistent
567 [[ -v assoc[$key] ]]
568 echo nonexistent=$?
569
570 ## STDOUT:
571 empty=0
572 k=0
573 nonexistent=1
574 ---
575 empty=0
576 k=0
577 nonexistent=1
578 ---
579 empty=0
580 k=0
581 nonexistent=1
582 ## END
583
584 ## N-I mksh status: 1
585 ## N-I mksh STDOUT:
586 ## END