1 #!/usr/bin/env bash
2
3 #### [[ glob matching, [[ has no glob expansion
4 [[ foo.py == *.py ]] && echo true
5 [[ foo.p == *.py ]] || echo false
6 ## stdout-json: "true\nfalse\n"
7
8 #### [[ glob matching with escapes
9 [[ 'foo.*' == *."*" ]] && echo true
10 # note that the pattern arg to fnmatch should be '*.\*'
11 ## stdout: true
12
13 #### equality
14 [[ '*.py' == '*.py' ]] && echo true
15 [[ foo.py == '*.py' ]] || echo false
16 ## stdout-json: "true\nfalse\n"
17
18 #### [[ glob matching with unquoted var
19 pat=*.py
20 [[ foo.py == $pat ]] && echo true
21 [[ foo.p == $pat ]] || echo false
22 ## stdout-json: "true\nfalse\n"
23
24 #### [[ regex matching
25 # mksh doesn't have this syntax of regex matching. I guess it comes from perl?
26 regex='.*\.py'
27 [[ foo.py =~ $regex ]] && echo true
28 [[ foo.p =~ $regex ]] || echo false
29 ## stdout-json: "true\nfalse\n"
30 ## N-I mksh stdout-json: ""
31 ## N-I mksh status: 1
32
33 #### [[ regex syntax error
34 # hm, it doesn't show any error, but it exits 2.
35 [[ foo.py =~ * ]] && echo true
36 ## status: 2
37 ## N-I mksh status: 1
38
39 #### [[ has no word splitting
40 var='one two'
41 [[ 'one two' == $var ]] && echo true
42 ## stdout: true
43
44 #### [[ has quote joining
45 var='one two'
46 [[ 'one 'tw"o" == $var ]] && echo true
47 ## stdout: true
48
49 #### [[ empty string is false
50 [[ 'a' ]] && echo true
51 [[ '' ]] || echo false
52 ## stdout-json: "true\nfalse\n"
53
54 #### && chain
55 [[ t && t && '' ]] || echo false
56 ## stdout: false
57
58 #### || chain
59 [[ '' || '' || t ]] && echo true
60 ## stdout: true
61
62 #### [[ compound expressions
63 # Notes on whitespace:
64 # - 1 and == need space seprating them, but ! and ( don't.
65 # - [[ needs whitesapce after it, but ]] doesn't need whitespace before it!
66 [[ ''||! (1 == 2)&&(2 == 2)]] && echo true
67 ## stdout: true
68
69 # NOTE on the two cases below. We're comparing
70 # (a || b) && c vs. a || (b && c)
71 #
72 # a = true, b = false, c = false is an example where they are different.
73 # && and || have precedence inside
74
75 #### precedence of && and || inside [[
76 [[ True || '' && '' ]] && echo true
77 ## stdout: true
78
79 #### precedence of && and || in a command context
80 if test True || test '' && test ''; then
81 echo YES
82 else
83 echo "NO precedence"
84 fi
85 ## stdout: NO precedence
86
87 # http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
88
89 #### Octal literals with -eq
90 shopt -u strict_arith || true
91 decimal=15
92 octal=017 # = 15 (decimal)
93 [[ $decimal -eq $octal ]] && echo true
94 [[ $decimal -eq ZZZ$octal ]] || echo false
95 ## STDOUT:
96 true
97 false
98 ## END
99 ## N-I mksh stdout: false
100 # mksh doesn't implement this syntax for literals.
101
102 #### Hex literals with -eq
103 shopt -u strict_arith || true
104 decimal=15
105 hex=0x0f # = 15 (decimal)
106 [[ $decimal -eq $hex ]] && echo true
107 [[ $decimal -eq ZZZ$hex ]] || echo false
108 ## stdout-json: "true\nfalse\n"
109 ## N-I mksh stdout: false
110
111 # TODO: Add tests for this
112 # https://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions
113 # When used with [[, the ‘<’ and ‘>’ operators sort lexicographically using the
114 # current locale. The test command uses ASCII ordering.
115
116 #### > on strings
117 # NOTE: < doesn't need space, even though == does? That's silly.
118 [[ b>a ]] && echo true
119 [[ b<a ]] || echo false
120 ## stdout-json: "true\nfalse\n"
121
122 #### != on strings
123 # NOTE: b!=a does NOT work
124 [[ b != a ]] && echo true
125 [[ a != a ]] || echo false
126 ## stdout-json: "true\nfalse\n"
127
128 #### -eq on strings
129 # This is lame behavior: it does a conversion to 0 first for any string
130 shopt -u strict_arith || true
131 [[ a -eq a ]] && echo true
132 [[ a -eq b ]] && echo true
133 ## STDOUT:
134 true
135 true
136 ## END
137
138 #### [[ compare with literal -f (compare with test-builtin.test.sh)
139 var=-f
140 [[ $var == -f ]] && echo true
141 [[ '-f' == $var ]] && echo true
142 ## stdout-json: "true\ntrue\n"
143
144 #### [[ with op variable (compare with test-builtin.test.sh)
145 # Parse error -- parsed BEFORE evaluation of vars
146 op='=='
147 [[ a $op a ]] && echo true
148 [[ a $op b ]] || echo false
149 ## status: 2
150 ## OK mksh status: 1
151
152 #### [[ with unquoted empty var (compare with test-builtin.test.sh)
153 empty=''
154 [[ $empty == '' ]] && echo true
155 ## stdout: true
156
157 #### [[ at runtime doesn't work
158 dbracket=[[
159 $dbracket foo == foo ]]
160 ## status: 127
161
162 #### [[ with env prefix doesn't work
163 FOO=bar [[ foo == foo ]]
164 ## status: 127
165
166 #### [[ over multiple lines is OK
167 # Hm it seems you can't split anywhere?
168 [[ foo == foo
169 && bar == bar
170 ]] && echo true
171 ## status: 0
172 ## STDOUT:
173 true
174 ## END
175
176 #### Argument that looks like a command word operator
177 [[ -f -f ]] || echo false
178 [[ -f == ]] || echo false
179 ## STDOUT:
180 false
181 false
182 ## END
183
184 #### Argument that looks like a real operator
185 [[ -f < ]] && echo 'should be parse error'
186 ## status: 2
187 ## OK mksh status: 1
188
189 #### User array compared to "$@" (broken unless shopt -s strict_array)
190 # Both are coerced to string! It treats it more like an UNQUOTED ${a[@]}.
191
192 a=('1 3' 5)
193 b=(1 2 3)
194 set -- 1 '3 5'
195 [[ "$@" = "${a[@]}" ]] && echo true
196 [[ "$@" = "${b[@]}" ]] || echo false
197 ## STDOUT:
198 true
199 false
200 ## END
201
202 #### Array coerces to string (shopt -s strict_array to disallow)
203 a=('1 3' 5)
204 [[ '1 3 5' = "${a[@]}" ]] && echo true
205 [[ '1 3 4' = "${a[@]}" ]] || echo false
206 ## STDOUT:
207 true
208 false
209 ## END
210
211 #### (( array1 == array2 )) doesn't work
212 a=('1 3' 5)
213 b=('1 3' 5)
214 c=('1' '3 5')
215 d=('1' '3 6')
216
217 # shells EXPAND a and b first
218 (( a == b ))
219 echo status=$?
220
221 (( a == c ))
222 echo status=$?
223
224 (( a == d ))
225 echo status=$?
226
227 ## stdout-json: ""
228 ## status: 1
229 ## BUG bash STDOUT:
230 status=1
231 status=1
232 status=1
233 ## END
234 ## BUG bash status: 0
235
236 #### Quotes don't matter in comparison
237 [[ '3' = 3 ]] && echo true
238 [[ '3' -eq 3 ]] && echo true
239 ## STDOUT:
240 true
241 true
242 ## END
243
244 #### -eq does dynamic arithmetic parsing (not supported in OSH)
245 [[ 1+2 -eq 3 ]] && echo true
246 expr='1+2'
247 [[ $expr -eq 3 ]] && echo true # must be dynamically parsed
248 ## STDOUT:
249 true
250 true
251 ## END
252 ## N-I osh stdout-json: ""
253 ## N-I osh status: 1
254
255 #### -eq coercion produces weird results
256 shopt -u strict_arith || true
257 [[ '' -eq 0 ]] && echo true
258 ## stdout: true
259
260 #### [[ '(' ]] is treated as literal
261 [[ '(' ]]
262 echo status=$?
263 ## stdout: status=0
264
265 #### [[ '(' foo ]] is syntax error
266 [[ '(' foo ]]
267 echo status=$?
268 ## status: 2
269 ## OK mksh status: 1
270
271 #### empty ! is treated as literal
272 [[ '!' ]]
273 echo status=$?
274 ## stdout: status=0
275
276 #### [[ -z ]] is syntax error
277 [[ -z ]]
278 echo status=$?
279 ## status: 2
280 ## OK mksh status: 1
281
282 #### [[ -z '>' ]]
283 [[ -z '>' ]] || echo false # -z is operator
284 ## stdout: false
285
286 #### [[ -z '>' a ]] is syntax error
287 [[ -z '>' -- ]]
288 echo status=$?
289 ## status: 2
290 ## OK mksh status: 1
291
292 #### test whether ']]' is empty
293 [[ ']]' ]]
294 echo status=$?
295 ## status: 0
296
297 #### [[ ]] is syntax error
298 [[ ]]
299 echo status=$?
300 ## stdout-json: ""
301 ## status: 2
302 ## OK mksh status: 1
303
304 #### [[ && ]] is syntax error
305 [[ && ]]
306 echo status=$?
307 ## stdout-json: ""
308 ## status: 2
309 ## OK mksh status: 1
310
311 #### [[ a 3< b ]] doesn't work (bug regression)
312 [[ a 3< b ]]
313 echo status=$?
314 [[ a 3> b ]]
315 echo status=$?
316 ## status: 2
317
318 # Hm these shells use the same redirect trick that OSH used to!
319
320 ## BUG mksh/zsh status: 0
321 ## BUG mksh/zsh STDOUT:
322 status=0
323 status=1
324 ## END
325
326 #### tilde expansion in [[
327 HOME=/home/bob
328 [[ ~ == /home/bob ]]
329 echo status=$?
330
331 [[ ~ == */bob ]]
332 echo status=$?
333
334 [[ ~ == */z ]]
335 echo status=$?
336
337 ## STDOUT:
338 status=0
339 status=0
340 status=1
341 ## END