Monday, April 20, 2009

Episode #25: My Shell Does Math

Ed says:

On Linux and Unix, I frequently rely on bc to do math at the command line. But, bc isn't built-in to Windows. A lot of people don't realize that the cmd.exe shell has some built-in capabilities for doing math... really lame math. But still, if you are caught in a pinch, don't wanna invoke the calc.exe GUI, and want to focus on integer math, cmd.exe can do your bidding. It's all invoked with the "set /a" command, as in:
C:\> set /a 2+2
Sexy, huh? Well, keep in mind that it is integer math only, as illustrated by:
C:\> set /a 3/2
That leads to some weirdness, as in:
C:\> set /a 3/2*2
Also, we're working only with signed 32-bit numbers here, so make sure your results stay under approximately two billion. Bigger than that will either push you into negative territory, or outright give you an error message:
C:\>set /a 1000000000*2

c:\test>set /a 2000000000*2

c:\test>set /a 3000000000*2
Invalid number. Numbers are limited to 32-bits of precision.

We've also got some options for using hex (specified with 0x as a prefix):
C:\> set /a 0xA + 0x2
Or, you can do octal, by prefacing your numbers with a zero:
C:\> set /a 010 + 01
You can even mix and match:
C:\> set /a 0xA + 01 + 1
It gets exciting to think that you can do hex or octal math at the cmd.exe command line, until you realize that all of your outputs are in, well, decimal. What genius thought up adding hex and octal as input, without providing options for hex and octal in the output?

Also, we've got bitwise AND with &, bitwise OR with |, and XOR with ^. But, again, the output is in... ugh... decimal.

Who uses decimal anymore? I mean, when you are driving, those speed limit signs are all in hex, right? Cruising around at 0x65 mph or 0x100 km/hr can be very exciting. When pulled over, tell the officer that you thought the speed limit sign was hexadecimal. Of course, you'll have to say that you assumed your speedometer is in decimal... kinda like set /a. Inputs in hex, outputs in decimal. See if that'll get you out of a ticket. Good luck.

Anyway, for really simple integer math of smallish numbers, set /a can come in handy. I do sometimes use it if I've gotta do a quick integer calculation and I don't wanna take focus off of my cmd.exe on the screen.

Honestly, given the quite limited abilities for cmd.exe to do math this way, I'm kind of expecting my command line kung fu sparring partners working in bash to knock me out with this one. But, in leading with my jaw, I'm hoping in the process that we'll all learn some completely magical fu from them when doing math in bash. So, lay it on us, guys. What fu have you?

Mr. Bucket read the draft of this post, and kicked in some interesting fu from the dawn of time for doing some hex match using built-in Windows command line tools. I remembered this one, but barely. It was buried in the dark recesses of my mind... I haven't used this since I bought a really good hex calculator a long long time ago.

Mr. Bucket suggested running the built-in Windows debugger tool:
C:\> debug

Then, at the goofy little "-" prompt, you could type:
- H 4a44 90
The debugger will then print out the sum and difference of the two numbers you provided:
4AD4  49B4
So there.... next time you are stranded on a desert island with only a Windows box and need to do hex addition or subtraction to hack your way off, you'll be all set. Thanks for the interesting point, Mr. Bucket!

Paul Says:

On UNIX/Linux systems I've always found that the bc command was available to me if I need to perform simple math (or even that "new" math people talk about):

$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.

It can even do fancy things like square root:

$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.

Of course Bash itself can do math as well. This is handy in shell scripts. Below is a command line "one-liner" example:

$ let RESULT=2+2 ; echo $RESULT

As long as "2+2" continues to equal 4 the universe stays in balance and life is good :) Of course, if you leave out the "let" directive you get very different results:

$ RESULT=2+2 ; echo $RESULT

Here "2+2" is being treated as a string, and therefore no math will be performed. So remember, when using Bash always "Let there be math".

Hal Says:

Ummmm, Paul? How about just:

$ echo $((2+2))

Using "$((...))" to put arithmetic expressions in-line is a heck of a lot easier than that tedious "let ..." nonsense.

By the way, for those of you that aren't using bash or a more modern shell with built-in arithmetic, there's also the really old-school expr command in addition to bc:

$ expr 2 + 2