1 #!/usr/bin/env bash
2 #
3 # Interesting interpretation of constants.
4 #
5 # "Constants with a leading 0 are interpreted as octal numbers. A leading ‘0x’
6 # or ‘0X’ denotes hexadecimal. Otherwise, numbers take the form [base#]n, where
7 # the optional base is a decimal number between 2 and 64 representing the
8 # arithmetic base, and n is a number in that base. If base# is omitted, then
9 # base 10 is used. When specifying n, the digits greater than 9 are represented
10 # by the lowercase letters, the uppercase letters, ‘@’, and ‘_’, in that order.
11 # If base is less than or equal to 36, lowercase and uppercase letters may be
12 # used interchangeably to represent numbers between 10 and 35. "
13 #
14 # NOTE $(( 8#9 )) can fail, and this can be done at parse time...
15
16 #### Side Effect in Array Indexing
17 a=(4 5 6)
18 echo "${a[b=2]} b=$b"
19 ## stdout: 6 b=2
20 ## OK zsh stdout: 5 b=2
21 ## N-I dash stdout-json: ""
22 ## N-I dash status: 2
23
24 #### Add one to var
25 i=1
26 echo $(($i+1))
27 ## stdout: 2
28
29 #### $ is optional
30 i=1
31 echo $((i+1))
32 ## stdout: 2
33
34 #### SimpleVarSub within arith
35 echo $(($j + 1))
36 ## stdout: 1
37
38 #### BracedVarSub within ArithSub
39 echo $((${j:-5} + 1))
40 ## stdout: 6
41
42 #### Arith word part
43 foo=1; echo $((foo+1))bar$(($foo+1))
44 ## stdout: 2bar2
45
46 #### Arith sub with word parts
47 # Making 13 from two different kinds of sub. Geez.
48 echo $((1 + $(echo 1)${undefined:-3}))
49 ## stdout: 14
50
51 #### Constant with quotes like '1'
52 # NOTE: Compare with [[. That is a COMMAND level expression, while this is a
53 # WORD level expression.
54 echo $(('1' + 2))
55 ## status: 0
56 ## N-I bash/zsh status: 1
57 ## N-I dash status: 2
58
59 #### Arith sub within arith sub
60 # This is unnecessary but works in all shells.
61 echo $((1 + $((2 + 3)) + 4))
62 ## stdout: 10
63
64 #### Backticks within arith sub
65 # This is unnecessary but works in all shells.
66 echo $((`echo 1` + 2))
67 ## stdout: 3
68
69 #### Invalid string to int
70 # bash, mksh, and zsh all treat strings that don't look like numbers as zero.
71 s=foo
72 echo $((s+5))
73 ## OK dash stdout-json: ""
74 ## OK dash status: 2
75 ## OK bash/mksh/zsh/osh stdout: 5
76 ## OK bash/mksh/zsh/osh status: 0
77
78 #### Invalid string to int with strict-arith
79 set -o strict-arith || true
80 s=foo
81 echo $s
82 echo $((s+5))
83 echo 'should not get here'
84 ## status: 1
85 ## stdout-json: "foo\n"
86 ## N-I bash status: 0
87 ## N-I bash STDOUT:
88 foo
89 5
90 should not get here
91 ## END
92 ## N-I dash status: 2
93 ## N-I dash stdout-json: ""
94 ## N-I mksh status: 1
95 ## N-I mksh stdout-json: ""
96 ## N-I zsh status: 1
97 ## N-I zsh stdout-json: ""
98
99 #### Newline in the middle of expression
100 echo $((1
101 + 2))
102 ## stdout: 3
103
104 #### Ternary operator
105 a=1
106 b=2
107 echo $((a>b?5:10))
108 ## stdout: 10
109
110 #### Preincrement
111 a=4
112 echo $((++a))
113 echo $a
114 ## stdout-json: "5\n5\n"
115 ## N-I dash status: 0
116 ## N-I dash stdout-json: "4\n4\n"
117
118 #### Postincrement
119 a=4
120 echo $((a++))
121 echo $a
122 ## stdout-json: "4\n5\n"
123 ## N-I dash status: 2
124 ## N-I dash stdout-json: ""
125
126 #### Increment undefined variables
127 (( undef1++ ))
128 (( ++undef2 ))
129 echo "[$undef1][$undef2]"
130 ## stdout: [1][1]
131 ## N-I dash stdout-json: "[][]\n"
132
133 #### Increment and decrement array
134 a=(5 6 7 8)
135 (( a[0]++, ++a[1], a[2]--, --a[3] ))
136 (( undef[0]++, ++undef[1], undef[2]--, --undef[3] ))
137 echo "${a[@]}" - "${undef[@]}"
138 ## stdout: 6 7 6 7 - 1 1 -1 -1
139 ## N-I dash stdout-json: ""
140 ## N-I dash status: 2
141 ## BUG zsh stdout: 5 6 7 8 -
142
143 #### Increment undefined variables with nounset
144 set -o nounset
145 (( undef1++ ))
146 (( ++undef2 ))
147 echo "[$undef1][$undef2]"
148 ## stdout-json: ""
149 ## status: 1
150 ## OK dash status: 2
151 ## BUG mksh/zsh status: 0
152 ## BUG mksh/zsh stdout-json: "[1][1]\n"
153
154 #### Comma operator (borrowed from C)
155 a=1
156 b=2
157 echo $((a,(b+1)))
158 ## stdout: 3
159 ## N-I dash status: 2
160 ## N-I dash stdout-json: ""
161
162 #### Augmented assignment
163 a=4
164 echo $((a+=1))
165 echo $a
166 ## stdout-json: "5\n5\n"
167
168 #### Comparison Ops
169 echo $(( 1 == 1 ))
170 echo $(( 1 != 1 ))
171 echo $(( 1 < 1 ))
172 echo $(( 1 <= 1 ))
173 echo $(( 1 > 1 ))
174 echo $(( 1 >= 1 ))
175 ## stdout-json: "1\n0\n0\n1\n0\n1\n"
176
177 #### Logical Ops
178 echo $((1 || 2))
179 echo $((1 && 2))
180 echo $((!(1 || 2)))
181 ## stdout-json: "1\n1\n0\n"
182
183 #### Logical Ops Short Circuit
184 x=11
185 (( 1 || (x = 22) ))
186 echo $x
187 (( 0 || (x = 33) ))
188 echo $x
189 (( 0 && (x = 44) ))
190 echo $x
191 (( 1 && (x = 55) ))
192 echo $x
193 ## stdout-json: "11\n33\n33\n55\n"
194 ## N-I dash stdout-json: "11\n11\n11\n11\n"
195
196 #### Bitwise ops
197 echo $((1|2))
198 echo $((1&2))
199 echo $((1^2))
200 echo $((~(1|2)))
201 ## stdout-json: "3\n0\n3\n-4\n"
202
203 #### Unary minus and plus
204 a=1
205 b=3
206 echo $((- a + + b))
207 ## stdout-json: "2\n"
208
209 #### No floating point
210 echo $((1 + 2.3))
211 ## status: 2
212 ## OK bash/mksh status: 1
213 ## BUG zsh status: 0
214
215 #### Array indexing in arith
216 # zsh does 1-based indexing!
217 array=(1 2 3 4)
218 echo $((array[1] + array[2]*3))
219 ## stdout: 11
220 ## OK zsh stdout: 7
221 ## N-I dash status: 2
222 ## N-I dash stdout-json: ""
223
224 #### Constants in base 36
225 echo $((36#a))-$((36#z))
226 ## stdout: 10-35
227 ## N-I dash stdout-json: ""
228 ## N-I dash status: 2
229
230 #### Constants in bases 2 to 64
231 # This is a truly bizarre syntax. Oh it comes from zsh... which allows 36.
232 echo $((64#a))-$((64#z)), $((64#A))-$((64#Z)), $((64#@)), $(( 64#_ ))
233 ## stdout: 10-35, 36-61, 62, 63
234 ## N-I dash stdout-json: ""
235 ## N-I dash status: 2
236 ## N-I mksh/zsh stdout-json: ""
237 ## N-I mksh/zsh status: 1
238
239 #### Dynamic base constants
240 base=16
241 echo $(( ${base}#a ))
242 ## stdout: 10
243 ## N-I dash stdout-json: ""
244 ## N-I dash status: 2
245
246 #### Octal constant
247 echo $(( 011 ))
248 ## stdout: 9
249 ## N-I mksh/zsh stdout: 11
250
251 #### Dynamic octal constant
252 zero=0
253 echo $(( ${zero}11 ))
254 ## stdout: 9
255 ## N-I mksh/zsh stdout: 11
256
257 #### Dynamic hex constants
258 zero=0
259 echo $(( ${zero}xAB ))
260 ## stdout: 171
261
262 #### Dynamic var names - result of runtime parse/eval
263 foo=5
264 x=oo
265 echo $(( foo + f$x + 1 ))
266 ## stdout: 11
267
268 #### Bizarre recursive name evaluation - result of runtime parse/eval
269 foo=5
270 bar=foo
271 spam=bar
272 eggs=spam
273 echo $((foo+1)) $((bar+1)) $((spam+1)) $((eggs+1))
274 ## stdout: 6 6 6 6
275 ## N-I dash stdout-json: ""
276 ## N-I dash status: 2
277
278 #### nounset with arithmetic
279 set -o nounset
280 x=$(( y + 5 ))
281 echo "should not get here: x=${x:-<unset>}"
282 ## stdout-json: ""
283 ## status: 1
284 ## BUG dash/mksh/zsh stdout: should not get here: x=5
285 ## BUG dash/mksh/zsh status: 0
286
287 #### Integer Overflow
288 set -o nounset
289 echo $(( 999999 * 999999 * 999999 * 999999 ))
290 ## stdout: 999996000005999996000001
291 ## BUG dash/bash/zsh stdout: -1996229794797103359
292 ## BUG mksh stdout: -15640831
293
294 #### Invalid LValue
295 a=9
296 (( (a + 2) = 3 ))
297 echo $a
298 ## status: 2
299 ## stdout-json: ""
300 ## OK bash/mksh/zsh stdout: 9
301 ## OK bash/mksh/zsh status: 0
302 # dash doesn't implement assignment
303 ## N-I dash status: 2
304 ## N-I dash stdout-json: ""
305
306 #### Invalid LValue that looks like array
307 (( 1[2] = 3 ))
308 echo "status=$?"
309 ## status: 2
310 ## stdout-json: ""
311 ## OK bash stdout: status=1
312 ## OK bash status: 0
313 ## OK mksh/zsh stdout: status=2
314 ## OK mksh/zsh status: 0
315 ## N-I dash stdout: status=127
316 ## N-I dash status: 0
317
318 #### Invalid LValue: two sets of brackets
319 (( a[1][2] = 3 ))
320 echo "status=$?"
321 # shells treat this as a NON-fatal error
322 ## status: 2
323 ## stdout-json: ""
324 ## OK bash stdout: status=1
325 ## OK mksh/zsh stdout: status=2
326 ## OK bash/mksh/zsh status: 0
327 # dash doesn't implement assignment
328 ## N-I dash stdout: status=127
329 ## N-I dash status: 0
330
331 #### Operator Precedence
332 echo $(( 1 + 2*3 - 8/2 ))
333 ## stdout: 3
334
335 #### Exponentiation with **
336 echo $(( 3 ** 0 ))
337 echo $(( 3 ** 1 ))
338 echo $(( 3 ** 2 ))
339 ## STDOUT:
340 1
341 3
342 9
343 ## END
344 ## N-I dash stdout-json: ""
345 ## N-I dash status: 2
346 ## N-I mksh stdout-json: ""
347 ## N-I mksh status: 1
348
349 #### Exponentiation operator has buggy precedence
350 # NOTE: All shells agree on this, but R and Python give -9, which is more
351 # mathematically correct.
352 echo $(( -3 ** 2 ))
353 ## osh stdout: 9
354 ## N-I dash stdout-json: ""
355 ## N-I dash status: 2
356 ## N-I mksh stdout-json: ""
357 ## N-I mksh status: 1
358
359 #### Negative exponent
360 # bash explicitly disallows negative exponents!
361 echo $(( 2**-1 * 5 ))
362 ## stdout-json: ""
363 ## status: 1
364 ## OK zsh stdout: 2.5
365 ## OK zsh status: 0
366 ## N-I dash stdout-json: ""
367 ## N-I dash status: 2