1 # 2 # Interesting interpretation of constants. 3 # 4 # "Constants with a leading 0 are interpreted as octal numbers. A leading ‘0x’ 5 # or ‘0X’ denotes hexadecimal. Otherwise, numbers take the form [base#]n, where 6 # the optional base is a decimal number between 2 and 64 representing the 7 # arithmetic base, and n is a number in that base. If base# is omitted, then 8 # base 10 is used. When specifying n, the digits greater than 9 are represented 9 # by the lowercase letters, the uppercase letters, ‘@’, and ‘_’, in that order. 10 # If base is less than or equal to 36, lowercase and uppercase letters may be 11 # used interchangeably to represent numbers between 10 and 35. " 12 # 13 # NOTE \$(( 8#9 )) can fail, and this can be done at parse time... 14 15 #### Side Effect in Array Indexing 16 a=(4 5 6) 17 echo "\${a[b=2]} b=\$b" 18 ## stdout: 6 b=2 19 ## OK zsh stdout: 5 b=2 20 ## N-I dash stdout-json: "" 21 ## N-I dash status: 2 22 23 #### Add one to var 24 i=1 25 echo \$((\$i+1)) 26 ## stdout: 2 27 28 #### \$ is optional 29 i=1 30 echo \$((i+1)) 31 ## stdout: 2 32 33 #### SimpleVarSub within arith 34 j=0 35 echo \$((\$j + 42)) 36 ## stdout: 42 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 shopt -u strict_arith || true 72 s=foo 73 echo \$((s+5)) 74 ## OK dash stdout-json: "" 75 ## OK dash status: 2 76 ## OK bash/mksh/zsh/osh stdout: 5 77 ## OK bash/mksh/zsh/osh status: 0 78 79 #### Invalid string to int with strict_arith 80 shopt -s strict_arith || true 81 s=foo 82 echo \$s 83 echo \$((s+5)) 84 echo 'should not get here' 85 ## status: 1 86 ## STDOUT: 87 foo 88 ## END 89 ## OK dash status: 2 90 ## N-I bash/mksh/zsh STDOUT: 91 foo 92 5 93 should not get here 94 ## END 95 ## N-I bash/mksh/zsh status: 0 96 97 #### Newline in the middle of expression 98 echo \$((1 99 + 2)) 100 ## stdout: 3 101 102 #### Ternary operator 103 a=1 104 b=2 105 echo \$((a>b?5:10)) 106 ## stdout: 10 107 108 #### Preincrement 109 a=4 110 echo \$((++a)) 111 echo \$a 112 ## stdout-json: "5\n5\n" 113 ## N-I dash status: 0 114 ## N-I dash stdout-json: "4\n4\n" 115 116 #### Postincrement 117 a=4 118 echo \$((a++)) 119 echo \$a 120 ## stdout-json: "4\n5\n" 121 ## N-I dash status: 2 122 ## N-I dash stdout-json: "" 123 124 #### Increment undefined variables 125 shopt -u strict_arith || true 126 (( undef1++ )) 127 (( ++undef2 )) 128 echo "[\$undef1][\$undef2]" 129 ## stdout: [1][1] 130 ## N-I dash stdout: [][] 131 132 #### Increment and decrement array elements 133 shopt -u strict_arith || true 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 #### Multiple digit constants with base N 240 echo \$((10#0123)), \$((16#1b)) 241 ## stdout: 123, 27 242 ## N-I dash stdout-json: "" 243 ## N-I dash status: 2 244 245 #### Dynamic base constants 246 base=16 247 echo \$(( \${base}#a )) 248 ## stdout: 10 249 ## N-I dash stdout-json: "" 250 ## N-I dash status: 2 251 252 #### Octal constant 253 echo \$(( 011 )) 254 ## stdout: 9 255 ## N-I mksh/zsh stdout: 11 256 257 #### Dynamic octal constant 258 zero=0 259 echo \$(( \${zero}11 )) 260 ## stdout: 9 261 ## N-I mksh/zsh stdout: 11 262 263 #### Dynamic hex constants 264 zero=0 265 echo \$(( \${zero}xAB )) 266 ## stdout: 171 267 268 #### Dynamic var names - result of runtime parse/eval 269 foo=5 270 x=oo 271 echo \$(( foo + f\$x + 1 )) 272 ## stdout: 11 273 ## OK osh stdout: 6 274 275 #### Bizarre recursive name evaluation - result of runtime parse/eval 276 foo=5 277 bar=foo 278 spam=bar 279 eggs=spam 280 echo \$((foo+1)) \$((bar+1)) \$((spam+1)) \$((eggs+1)) 281 ## stdout: 6 6 6 6 282 ## OK osh stdout: 6 1 1 1 283 ## N-I dash stdout-json: "" 284 ## N-I dash status: 2 285 286 #### nounset with arithmetic 287 set -o nounset 288 x=\$(( y + 5 )) 289 echo "should not get here: x=\${x:-}" 290 ## stdout-json: "" 291 ## status: 1 292 ## BUG dash/mksh/zsh stdout: should not get here: x=5 293 ## BUG dash/mksh/zsh status: 0 294 295 #### Integer Overflow 296 set -o nounset 297 echo \$(( 999999 * 999999 * 999999 * 999999 )) 298 ## stdout: 999996000005999996000001 299 ## BUG dash/bash/zsh stdout: -1996229794797103359 300 ## BUG mksh stdout: -15640831 301 302 #### Invalid LValue 303 a=9 304 (( (a + 2) = 3 )) 305 echo \$a 306 ## status: 2 307 ## stdout-json: "" 308 ## OK bash/mksh/zsh stdout: 9 309 ## OK bash/mksh/zsh status: 0 310 # dash doesn't implement assignment 311 ## N-I dash status: 2 312 ## N-I dash stdout-json: "" 313 314 #### Invalid LValue that looks like array 315 (( 1[2] = 3 )) 316 echo "status=\$?" 317 ## status: 2 318 ## stdout-json: "" 319 ## OK bash stdout: status=1 320 ## OK bash status: 0 321 ## OK mksh/zsh stdout: status=2 322 ## OK mksh/zsh status: 0 323 ## N-I dash stdout: status=127 324 ## N-I dash status: 0 325 326 #### Invalid LValue: two sets of brackets 327 (( a[1][2] = 3 )) 328 echo "status=\$?" 329 # shells treat this as a NON-fatal error 330 ## status: 2 331 ## stdout-json: "" 332 ## OK bash stdout: status=1 333 ## OK mksh/zsh stdout: status=2 334 ## OK bash/mksh/zsh status: 0 335 # dash doesn't implement assignment 336 ## N-I dash stdout: status=127 337 ## N-I dash status: 0 338 339 #### Operator Precedence 340 echo \$(( 1 + 2*3 - 8/2 )) 341 ## stdout: 3 342 343 #### Exponentiation with ** 344 echo \$(( 3 ** 0 )) 345 echo \$(( 3 ** 1 )) 346 echo \$(( 3 ** 2 )) 347 ## STDOUT: 348 1 349 3 350 9 351 ## END 352 ## N-I dash stdout-json: "" 353 ## N-I dash status: 2 354 ## N-I mksh stdout-json: "" 355 ## N-I mksh status: 1 356 357 #### Exponentiation operator has buggy precedence 358 # NOTE: All shells agree on this, but R and Python give -9, which is more 359 # mathematically correct. 360 echo \$(( -3 ** 2 )) 361 ## osh stdout: 9 362 ## N-I dash stdout-json: "" 363 ## N-I dash status: 2 364 ## N-I mksh stdout-json: "" 365 ## N-I mksh status: 1 366 367 #### Negative exponent 368 # bash explicitly disallows negative exponents! 369 echo \$(( 2**-1 * 5 )) 370 ## stdout-json: "" 371 ## status: 1 372 ## OK zsh stdout: 2.5 373 ## OK zsh status: 0 374 ## N-I dash stdout-json: "" 375 ## N-I dash status: 2 376 377 #### Comment not allowed in the middle of multiline arithmetic 378 echo \$(( 379 1 + 380 2 + \ 381 3 382 )) 383 echo \$(( 384 1 + 2 # not a comment 385 )) 386 (( a = 3 + 4 # comment 387 )) 388 echo [\$a] 389 ## status: 1 390 ## STDOUT: 391 6 392 ## END 393 ## OK dash/osh status: 2 394 ## OK bash STDOUT: 395 6 396 [] 397 ## END 398 ## OK bash status: 0 399 400 #### Can't add integer to indexed array 401 declare -a array=(1 2 3) 402 echo \$((array + 5)) 403 ## status: 1 404 ## stdout-json: "" 405 ## BUG bash status: 0 406 ## BUG bash STDOUT: 407 6 408 ## END 409 ## N-I dash status: 2 410 411 #### Can't add integer to associative array 412 typeset -A assoc 413 assoc[0]=42 414 echo \$((assoc + 5)) 415 ## status: 1 416 ## stdout-json: "" 417 ## BUG bash/mksh/zsh status: 0 418 ## BUG bash/mksh/zsh stdout: 47 419 ## BUG dash status: 0 420 ## BUG dash stdout: 5 421 422 #### Double subscript 423 a=(1 2 3) 424 echo \$(( a[1] )) 425 echo \$(( a[1][1] )) 426 ## status: 1 427 ## OK osh status: 2 428 ## STDOUT: 429 2 430 ## END 431 ## N-I dash status: 2 432 ## N-I dash stdout-json: "" 433 ## OK zsh STDOUT: 434 1 435 ## END 436 437 #### result of ArithSub is array 438 a=(4 5 6) 439 echo declared 440 b=\$(( a )) 441 echo \$b 442 ## status: 1 443 ## STDOUT: 444 declared 445 ## END 446 ## BUG bash/mksh status: 0 447 ## BUG bash/mksh STDOUT: 448 declared 449 4 450 ## END 451 ## N-I dash status: 2 452 ## N-I dash stdout-json: "" 453 454 #### result of ArithSub is assoc array 455 declare -A A=(['foo']=bar ['spam']=eggs) 456 echo declared 457 b=\$(( A )) 458 echo \$b 459 ## status: 1 460 ## STDOUT: 461 declared 462 ## END 463 ## N-I mksh stdout-json: "" 464 ## BUG bash/zsh status: 0 465 ## BUG bash/zsh STDOUT: 466 declared 467 0 468 ## END 469 ## N-I dash status: 2 470 ## N-I dash stdout-json: "" 471 472 #### comma operator 473 a=(4 5 6) 474 475 # zsh and osh can't evaluate the array like that 476 # which is consistent with their behavior on \$(( a )) 477 478 echo \$(( a, last = a[2], 42 )) 479 echo last=\$last 480 481 ## status: 1 482 ## stdout-json: "" 483 484 ## N-I dash status: 2 485 486 ## OK bash/mksh status: 0 487 ## OK bash/mksh STDOUT: 488 42 489 last=6 490 ## END 491 492 #### assignment with dynamic var name 493 shopt -s parse_dynamic_arith 494 foo=bar 495 echo \$(( x\$foo = 42 )) 496 echo xbar=\$xbar 497 ## STDOUT: 498 42 499 xbar=42 500 ## END 501 502 #### array assignment with dynamic array name 503 shopt -s parse_dynamic_arith 504 foo=bar 505 echo \$(( x\$foo[5] = 42 )) 506 echo 'xbar[5]='\${xbar[5]} 507 ## STDOUT: 508 42 509 xbar[5]=42 510 ## END 511 ## BUG zsh STDOUT: 512 42 513 xbar[5]= 514 ## END 515 ## N-I dash status: 2 516 ## N-I dash stdout-json: "" 517 518 #### unary assignment with dynamic var name 519 shopt -s parse_dynamic_arith 520 foo=bar 521 xbar=42 522 echo \$(( x\$foo++ )) 523 echo xbar=\$xbar 524 ## STDOUT: 525 42 526 xbar=43 527 ## END 528 ## BUG dash status: 2 529 ## BUG dash stdout-json: "" 530 531 #### unary array assignment with dynamic var name 532 shopt -s parse_dynamic_arith 533 foo=bar 534 xbar[5]=42 535 echo \$(( x\$foo[5]++ )) 536 echo 'xbar[5]='\${xbar[5]} 537 ## STDOUT: 538 42 539 xbar[5]=43 540 ## END 541 ## BUG zsh STDOUT: 542 0 543 xbar[5]=42 544 ## END 545 ## N-I dash status: 2 546 ## N-I dash stdout-json: "" 547 548 #### shopt -s eval_unsafe_arith 549 shopt -s eval_unsafe_arith 550 e=1+2 551 echo \$(( e + 3 )) 552 [[ e -eq 3 ]] && echo true 553 [ e -eq 3 ] 554 echo status=\$? 555 ## STDOUT: 556 6 557 true 558 status=2 559 ## END 560 ## BUG mksh STDOUT: 561 6 562 true 563 status=0 564 ## END 565 ## N-I dash status: 2 566 ## N-I dash stdout-json: "" 567 568 #### eval_unsafe_arith on empty string 569 shopt -s eval_unsafe_arith 570 a='' 571 echo \$(( a )) 572 573 a2=' ' 574 echo \$(( a2 )) 575 ## STDOUT: 576 0 577 0 578 ## END 579 580 #### nested ternary (bug fix) 581 echo \$((1?2?3:4:5)) 582 ## STDOUT: 583 3 584 ## END 585 586 #### 1 ? a=1 : b=2 ( bug fix) 587 echo \$((1 ? a=1 : 42 )) 588 echo a=\$a 589 590 # this does NOT work 591 #echo \$((1 ? a=1 : b=2 )) 592 593 ## STDOUT: 594 1 595 a=1 596 ## END 597 ## BUG zsh stdout-json: "" 598 ## BUG zsh status: 1 599 600 #### Invalid constant 601 602 echo \$((a + x42)) 603 echo status=\$? 604 605 # weird asymmetry -- the above is a syntax error, but this isn't 606 \$SH -c 'echo \$((a + 42x))' 607 echo status=\$? 608 609 # regression 610 shopt -s eval_unsafe_arith 611 echo \$((a + 42x)) 612 echo status=\$? 613 ## status: 1 614 ## STDOUT: 615 0 616 status=0 617 status=1 618 ## END 619 ## OK dash status: 2 620 ## OK dash STDOUT: 621 0 622 status=0 623 status=2 624 ## END 625 ## BUG bash status: 0 626 ## BUG bash STDOUT: 627 0 628 status=0 629 status=1 630 status=1 631 ## END