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:-}" 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