18th Oct 2017
Return codes, Functions, and the number 255
The problem with using shell functions to return integers
In most languages, you would write a function to return the square of an integer like this:
private int square(int n) { return n*n; }
This Java code would work fine; The Shell script would look like this; I've added a couple of test cases, too:
#!/bin/bash square() { return $(($1 * $1)) } for n in 4 8 15 16 17 do echo -n "The square of $n is: " square $n echo $? # The return code is passed back as $? done exit 12345download square.sh
The Shell script would print these results:
$ ./square.sh The square of 4 is: 16 The square of 8 is: 64 The square of 15 is: 225 The square of 16 is: 0 The square of 17 is: 33 $ echo $? 57 $
The observant reader will have spotted that it works fine up to, and including, 15*15 = 225.
16*16 is clearly not zero; it is 256.
And 17*17 is 289, not 33.
These wrong results are 256 below their expected values. That's because the return code from a shell function (and from any Unix/Linux program) is a single byte; it has a value between 0 and 255. So 256 gets wrapped around to zero again. 289 is 33 more than 256, so it gets wrapped to 33.
Because the whole script ends with "exit 12345
", the return code from the script is 57 (that's 12345 modulo 256
, aka 12345%256
). But not 12345.
The Solution
Oddly, the solution here, is to acknowledge that the shell really treats all variables as strings, until it is forced to do otherwise - for example, when it is forced to make a numerical comparison, such as:
if [ "$a" -gt "$b" ]; then ...
So if you echo
the result back as a string, this square
function will work fine.
In the code below, the syntax: "$(( expression ))
" tells the shell to evaluate expression
, and return the result.
(It would also be possible (and probably more compatible across shell implementations) to use: "expr $1 \* $1
" (the "*" has to have the backslash, or the shell would interpret it as 'all filenames in the current directory'), but that does involve spawning the external expr
program, rather than keeping it all in the bash
process.)
In the main code which calls the square
function, instead of the result coming to the variable "$?
", it is returned as standard output, so we catch its content by saying: "result=$(square $n)
" - this catches the output and stores it in the $result
variable.
#!/bin/bash square() { echo $(($1 * $1)) } for n in 4 8 15 16 17 do echo -n "The square of $n is: " result=$(square $n) echo $result # The return code is passed back as $? done exit 12345download square2.sh
The return code from the script, 12345
, will only ever be seen as 57
, because that is how Unix (and therefore Linux) works. The return code from a program is only ever a single byte.
And it's all smarter than this Java implementation by some ill-informed Java coder, as found somewhere on the internet (the guilty shall rename nameless):
Invest in your career. Buy my Shell Scripting Tutorial today: