Bash return code fun

Why local=$(exit 1) will always exit 0

So a few things with return codes mess up beginning bash programmers

  1. Creating variables always returns 0
  2. Return codes are a single byte int

Let me provide a few examples:

Declared Variables


[root@mytest10][~] cat footest1

function foo() {
   echo 'foobar'
   return 123
}
function bar() {
   local rtc #Create local variable for housing the return code
   local value=$(foo)
   rtc=$?
   echo ${value}
   echo ${rtc}
}
bar

[root@mytest10][~] bash footest1
foobar
0

Now you can see from the code the return code value from foo as seen in bar should have been 123. But instead it is 0.
This is purely because we assigned value at the same time we created the local variable. And since the explicit declaration of that local variable was successful $? is over written with that successes and 0 is returned. Unfortunately this is not intuitive or easy to track down if you don’t know what is happening in advance.

Here is the debug output

+ bash -x footest1
+ bar
+ local rtc
++ foo
++ echo foobar
++ return 123
+ local value=foobar
+ rtc=0
+ echo foobar
foobar
+ echo 0
0

Here is a counter example:

[root@mytest10][~] cat footest1
function foo() {
echo 'foobar'
return 123
}
function bar() {
local rtc #Create local variable for housing the return code
local value #Always declair variables before you assigne values
value=$(foo)
rtc=$?
echo ${value}
echo ${rtc}
}
bar
[root@mytest10][~] bash footest1
foobar
123

Now you can see you get the desired effect!

Conclusion: If you have special deceleration variables* you must declare said variables before assigning values.

*Special deceleration would be anything like local scope, declared array, read only, etc. The creation of standard global variables does not suffer from this overriding return code issue.

It is only an int

So when using bash we are often gluing things together from other applications. Those applications may return a wide, wide range of numeric error codes. You capture these and all is well… most of the time. The problem is when you exceed the range of 0-255 bash will start looping again. So when you get an error code of 512 (Perhaps for something like Auth controller unreachable) you are shocked to see you script think it is full of success and keeps on trucking.
This is because Bash sees the error of 512 as 0, or success. Here, take a look.

[root@mytest10][~] cat footest2
function foo() {
return $1
}
for i in 254 255 256 257 511 512 513 514 1023 1024 1025 1026; do
foo ${i}
bar=$?
echo $bar
done
[root@mytest10][~] bash footest2
254
255
0
1
255
0
1
2
255
0
1
2

Here is the debug output

+ for i in 254 255 256 257 511 512 513 514 1023 1024 1025 1026
+ foo 511
+ return 511
+ bar=255
+ echo 255
255
+ for i in 254 255 256 257 511 512 513 514 1023 1024 1025 1026
+ foo 512
+ return 512
+ bar=0
+ echo 0
0
+ for i in 254 255 256 257 511 512 513 514 1023 1024 1025 1026
+ foo 513
+ return 513
+ bar=1
+ echo 1
1

Common Bash Return Codes

Exit Code Number Meaning Example Comments
0 Catchall for success  true By default if you do nothing else and your script or function runs it will implicitly return 0
1 Catchall for general errors let “var1 = 1/0” Miscellaneous errors, such as “divide by zero” and other impermissible operations
2 Misuse of shell builtins (according to Bash documentation) empty_function() {} Missing keyword or command, or permission problem (and diff return code on a failed binary file comparison).
126 Command invoked cannot execute /dev/null Permission problem or command is not an executable
127 “command not found” illegal_command Possible problem with $PATH or a typo
128 Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 – 255 (see first footnote)
128+n Fatal error signal “n” kill -9 $PPID of script $? returns 137 (128 + 9)
130 Script terminated by Control-C Ctl-C Control-C is fatal error signal 2, (130 = 128 + 2, see above)
255* Exit status out of range exit -1 exit takes only integer args in the range 0 – 255

 

*Taken from http://www.tldp.org/LDP/abs/html/exitcodes.html

 

**This post is dedicated to my friends who did not suffer under the regime of PHP or Bash and thus did not dedicate 3 of the last five years into turning bash into full featured object oriented programming language and thus come to me seeking sanity every time something fails and they start hating life and all the legacy code written in bash –
Long live the new paradigm of Python and Go – May you reign of glory never end  😉

Leave a Reply

Your email address will not be published. Required fields are marked *