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 ## status: 1
84 ## stdout-json: "foo\n"
85 ## N-I bash status: 0
86 ## N-I bash stdout-json: "foo\n5\n"
87 ## N-I dash status: 2
88 ## N-I dash stdout-json: ""
89 ## N-I mksh status: 1
90 ## N-I mksh stdout-json: ""
91 ## N-I zsh status: 1
92 ## N-I zsh stdout-json: ""
93
94 #### Newline in the middle of expression
95 echo $((1
96 + 2))
97 ## stdout: 3
98
99 #### Ternary operator
100 a=1
101 b=2
102 echo $((a>b?5:10))
103 ## stdout: 10
104
105 #### Preincrement
106 a=4
107 echo $((++a))
108 echo $a
109 ## stdout-json: "5\n5\n"
110 ## N-I dash status: 0
111 ## N-I dash stdout-json: "4\n4\n"
112
113 #### Postincrement
114 a=4
115 echo $((a++))
116 echo $a
117 ## stdout-json: "4\n5\n"
118 ## N-I dash status: 2
119 ## N-I dash stdout-json: ""
120
121 #### Increment undefined variables
122 (( undef1++ ))
123 (( ++undef2 ))
124 echo "[$undef1][$undef2]"
125 ## stdout: [1][1]
126 ## N-I dash stdout-json: "[][]\n"
127
128 #### Increment and decrement array
129 a=(5 6 7 8)
130 (( a[0]++, ++a[1], a[2]--, --a[3] ))
131 (( undef[0]++, ++undef[1], undef[2]--, --undef[3] ))
132 echo "${a[@]}" - "${undef[@]}"
133 ## stdout: 6 7 6 7 - 1 1 -1 -1
134 ## N-I dash stdout-json: ""
135 ## N-I dash status: 2
136 ## BUG zsh stdout: 5 6 7 8 -
137
138 #### Increment undefined variables with nounset
139 set -o nounset
140 (( undef1++ ))
141 (( ++undef2 ))
142 echo "[$undef1][$undef2]"
143 ## stdout-json: ""
144 ## status: 1
145 ## OK dash status: 2
146 ## BUG mksh/zsh status: 0
147 ## BUG mksh/zsh stdout-json: "[1][1]\n"
148
149 #### Comma operator (borrowed from C)
150 a=1
151 b=2
152 echo $((a,(b+1)))
153 ## stdout: 3
154 ## N-I dash status: 2
155 ## N-I dash stdout-json: ""
156
157 #### Augmented assignment
158 a=4
159 echo $((a+=1))
160 echo $a
161 ## stdout-json: "5\n5\n"
162
163 #### Comparison Ops
164 echo $(( 1 == 1 ))
165 echo $(( 1 != 1 ))
166 echo $(( 1 < 1 ))
167 echo $(( 1 <= 1 ))
168 echo $(( 1 > 1 ))
169 echo $(( 1 >= 1 ))
170 ## stdout-json: "1\n0\n0\n1\n0\n1\n"
171
172 #### Logical Ops
173 echo $((1 || 2))
174 echo $((1 && 2))
175 echo $((!(1 || 2)))
176 ## stdout-json: "1\n1\n0\n"
177
178 #### Logical Ops Short Circuit
179 x=11
180 (( 1 || (x = 22) ))
181 echo $x
182 (( 0 || (x = 33) ))
183 echo $x
184 (( 0 && (x = 44) ))
185 echo $x
186 (( 1 && (x = 55) ))
187 echo $x
188 ## stdout-json: "11\n33\n33\n55\n"
189 ## N-I dash stdout-json: "11\n11\n11\n11\n"
190
191 #### Bitwise ops
192 echo $((1|2))
193 echo $((1&2))
194 echo $((1^2))
195 echo $((~(1|2)))
196 ## stdout-json: "3\n0\n3\n-4\n"
197
198 #### Unary minus and plus
199 a=1
200 b=3
201 echo $((- a + + b))
202 ## stdout-json: "2\n"
203
204 #### No floating point
205 echo $((1 + 2.3))
206 ## status: 2
207 ## OK bash/mksh status: 1
208 ## BUG zsh status: 0
209
210 #### Array indexing in arith
211 # zsh does 1-based indexing!
212 array=(1 2 3 4)
213 echo $((array[1] + array[2]*3))
214 ## stdout: 11
215 ## OK zsh stdout: 7
216 ## N-I dash status: 2
217 ## N-I dash stdout-json: ""
218
219 #### Constants in base 36
220 echo $((36#a))-$((36#z))
221 ## stdout: 10-35
222 ## N-I dash stdout-json: ""
223 ## N-I dash status: 2
224
225 #### Constants in bases 2 to 64
226 # This is a truly bizarre syntax. Oh it comes from zsh... which allows 36.
227 echo $((64#a))-$((64#z)), $((64#A))-$((64#Z)), $((64#@)), $(( 64#_ ))
228 ## stdout: 10-35, 36-61, 62, 63
229 ## N-I dash stdout-json: ""
230 ## N-I dash status: 2
231 ## N-I mksh/zsh stdout-json: ""
232 ## N-I mksh/zsh status: 1
233
234 #### Dynamic base constants
235 base=16
236 echo $(( ${base}#a ))
237 ## stdout: 10
238 ## N-I dash stdout-json: ""
239 ## N-I dash status: 2
240
241 #### Octal constant
242 echo $(( 011 ))
243 ## stdout: 9
244 ## N-I mksh/zsh stdout: 11
245
246 #### Dynamic octal constant
247 zero=0
248 echo $(( ${zero}11 ))
249 ## stdout: 9
250 ## N-I mksh/zsh stdout: 11
251
252 #### Dynamic hex constants
253 zero=0
254 echo $(( ${zero}xAB ))
255 ## stdout: 171
256
257 #### Dynamic var names - result of runtime parse/eval
258 foo=5
259 x=oo
260 echo $(( foo + f$x + 1 ))
261 ## stdout: 11
262
263 #### Bizarre recursive name evaluation - result of runtime parse/eval
264 foo=5
265 bar=foo
266 spam=bar
267 eggs=spam
268 echo $((foo+1)) $((bar+1)) $((spam+1)) $((eggs+1))
269 ## stdout: 6 6 6 6
270 ## N-I dash stdout-json: ""
271 ## N-I dash status: 2
272
273 #### nounset with arithmetic
274 set -o nounset
275 x=$(( y + 5 ))
276 echo "should not get here: x=${x:-<unset>}"
277 ## stdout-json: ""
278 ## status: 1
279 ## BUG dash/mksh/zsh stdout: should not get here: x=5
280 ## BUG dash/mksh/zsh status: 0
281
282 #### Integer Overflow
283 set -o nounset
284 echo $(( 999999 * 999999 * 999999 * 999999 ))
285 ## stdout: 999996000005999996000001
286 ## BUG dash/bash/zsh stdout: -1996229794797103359
287 ## BUG mksh stdout: -15640831
288
289 #### Invalid LValue
290 a=9
291 (( (a + 2) = 3 ))
292 echo $a
293 ## status: 2
294 ## stdout-json: ""
295 ## OK bash/mksh/zsh stdout: 9
296 ## OK bash/mksh/zsh status: 0
297 # dash doesn't implement assignment
298 ## N-I dash status: 2
299 ## N-I dash stdout-json: ""
300
301 #### Invalid LValue that looks like array
302 (( 1[2] = 3 ))
303 echo "status=$?"
304 ## status: 2
305 ## stdout-json: ""
306 ## OK bash stdout: status=1
307 ## OK bash status: 0
308 ## OK mksh/zsh stdout: status=2
309 ## OK mksh/zsh status: 0
310 ## N-I dash stdout: status=127
311 ## N-I dash status: 0
312
313 #### Invalid LValue: two sets of brackets
314 (( a[1][2] = 3 ))
315 echo "status=$?"
316 # shells treat this as a NON-fatal error
317 ## status: 2
318 ## stdout-json: ""
319 ## OK bash stdout: status=1
320 ## OK mksh/zsh stdout: status=2
321 ## OK bash/mksh/zsh status: 0
322 # dash doesn't implement assignment
323 ## N-I dash stdout: status=127
324 ## N-I dash status: 0
325