1 #!/bin/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 ### Add one to var
17 i=1
18 echo $(($i+1))
19 # stdout: 2
20
21 ### $ is optional
22 i=1
23 echo $((i+1))
24 # stdout: 2
25
26 ### Bizarre recursive evaluation rule
27 foo=5
28 bar=foo
29 spam=bar
30 eggs=spam
31 echo $((foo+1)) $((bar+1)) $((spam+1)) $((eggs+1))
32 # stdout: 6 6 6 6
33 # N-I dash stdout-json: ""
34 # N-I dash status: 2
35
36 ### SimpleVarSub within arith
37 echo $(($j + 1))
38 # stdout: 1
39
40 ### BracedVarSub within ArithSub
41 echo $((${j:-5} + 1))
42 # stdout: 6
43
44 ### Arith word part
45 foo=1; echo $((foo+1))bar$(($foo+1))
46 # stdout: 2bar2
47
48 ### Arith sub with word parts
49 # Making 13 from two different kinds of sub. Geez.
50 echo $((1 + $(echo 1)${undefined:-3}))
51 # stdout: 14
52
53 ### Constant with quotes like '1'
54 # NOTE: Compare with [[. That is a COMMAND level expression, while this is a
55 # WORD level expression.
56 echo $(('1' + 2))
57 # status: 0
58 # N-I bash/zsh status: 1
59 # N-I dash status: 2
60
61 ### Arith sub within arith sub
62 # This is unnecessary but works in all shells.
63 echo $((1 + $((2 + 3)) + 4))
64 # stdout: 10
65
66 ### Backticks within arith sub
67 # This is unnecessary but works in all shells.
68 echo $((`echo 1` + 2))
69 # stdout: 3
70
71 ### Invalid string to int
72 # bash, mksh, and zsh all treat strings that don't look like numbers as zero.
73 s=foo
74 echo $((s+5))
75 # OK dash stdout-json: ""
76 # OK dash status: 2
77 # OK bash/mksh/zsh/osh stdout: 5
78 # OK bash/mksh/zsh/osh status: 0
79
80 ### Invalid string to int with strict-arith
81 set -o strict-arith || true
82 s=foo
83 echo $s
84 echo $((s+5))
85 # status: 1
86 # stdout-json: "foo\n"
87 # N-I bash status: 0
88 # N-I bash stdout-json: "foo\n5\n"
89 # N-I dash status: 2
90 # N-I dash stdout-json: ""
91 # N-I mksh status: 1
92 # N-I mksh stdout-json: ""
93 # N-I zsh status: 1
94 # N-I zsh stdout-json: ""
95
96 ### Newline in the middle of expression
97 echo $((1
98 + 2))
99 # stdout: 3
100
101 ### Ternary operator
102 a=1
103 b=2
104 echo $((a>b?5:10))
105 # stdout: 10
106
107 ### Preincrement
108 a=4
109 echo $((++a))
110 echo $a
111 # stdout-json: "5\n5\n"
112 # N-I dash status: 0
113 # N-I dash stdout-json: "4\n4\n"
114
115 ### Postincrement
116 a=4
117 echo $((a++))
118 echo $a
119 # stdout-json: "4\n5\n"
120 # N-I dash status: 2
121 # N-I dash stdout-json: ""
122
123 ### Comma operator (borrowed from C)
124 a=1
125 b=2
126 echo $((a,(b+1)))
127 # stdout: 3
128 # N-I dash status: 2
129 # N-I dash stdout-json: ""
130
131 ### Mutating ops
132 a=4
133 echo $((a+=1))
134 echo $a
135 # stdout-json: "5\n5\n"
136
137 ### Bitwise ops
138 echo $((1|2))
139 echo $((1&2))
140 echo $((~(1|2)))
141 # stdout-json: "3\n0\n-4\n"
142
143 ### Unary minus and plus
144 a=1
145 b=3
146 echo $((- a + + b))
147 # stdout-json: "2\n"
148
149 ### No floating point
150 echo $((1 + 2.3))
151 # status: 1
152 # OK dash status: 2
153 # BUG zsh status: 0
154
155 ### Array indexing in arith
156 # zsh does 1-based indexing!
157 array=(1 2 3 4)
158 echo $((array[1] + array[2]*3))
159 # stdout: 11
160 # OK zsh stdout: 7
161 # N-I dash status: 2
162 # N-I dash stdout-json: ""
163
164 ### Constants in base 36
165 echo $((36#a))-$((36#z))
166 # stdout: 10-35
167 # N-I dash stdout-json: ""
168 # N-I dash status: 2
169
170 ### Constants in bases 2 to 64
171 # This is a truly bizarre syntax. Oh it comes from zsh... which allows 36.
172 echo $((64#a))-$((64#z)), $((64#A))-$((64#Z)), $((64#@)), $(( 64#_ ))
173 # stdout: 10-35, 36-61, 62, 63
174 # N-I dash stdout-json: ""
175 # N-I dash status: 2
176 # N-I mksh/zsh stdout-json: ""
177 # N-I mksh/zsh status: 1
178
179 ### Dynamic base constants
180 base=16
181 echo $(( ${base}#a ))
182 # stdout: 10
183 # N-I dash stdout-json: ""
184 # N-I dash status: 2
185
186 ### Octal constant
187 echo $(( 011 ))
188 # stdout: 9
189 # N-I mksh/zsh stdout: 11
190
191 ### Dynamic octal constant
192 zero=0
193 echo $(( ${zero}11 ))
194 # stdout: 9
195 # N-I mksh/zsh stdout: 11
196
197 ### Dynamic hex constants
198 zero=0
199 echo $(( ${zero}xAB ))
200 # stdout: 171
201
202 ### Dynamic var names!
203 foo=5
204 x=oo
205 echo $(( foo + f$x + 1 ))
206 # stdout: 11
207
208 ### nounset with arithmetic
209 set -o nounset
210 x=$(( y + 5 ))
211 echo "should not get here: x=${x:-<unset>}"
212 # stdout-json: ""
213 # status: 1
214 # BUG dash/mksh/zsh stdout: should not get here: x=5
215 # BUG dash/mksh/zsh status: 0
216
217