Linux return exit code

Linux return exit code

Exit codes are a number between 0 and 255, which is returned by any Unix command when it returns control to its parent process.
Other numbers can be used, but these are treated modulo 256, so exit -10 is equivalent to exit 246 , and exit 257 is equivalent to exit 1 .

These can be used within a shell script to change the flow of execution depending on the success or failure of commands executed. This was briefly introduced in Variables — Part II. Here we shall look in more detail in the available interpretations of exit codes.

Success is traditionally represented with exit 0 ; failure is normally indicated with a non-zero exit-code. This value can indicate different reasons for failure.
For example, GNU grep returns 0 on success, 1 if no matches were found, and 2 for other errors (syntax errors, non-existent input files, etc).

We shall look at three different methods for checking error status, and discuss the pros and cons of each approach.

Firstly, the simple approach:

#!/bin/sh # First attempt at checking return codes USERNAME=`grep "^$:" /etc/passwd|cut -d":" -f1` if [ "$?" -ne "0" ]; then echo "Sorry, cannot find user $ in /etc/passwd" exit 1 fi NAME=`grep "^$:" /etc/passwd|cut -d":" -f5` HOMEDIR=`grep "^$:" /etc/passwd|cut -d":" -f6` echo "USERNAME: $USERNAME" echo "NAME: $NAME" echo "HOMEDIR: $HOMEDIR"

This script works fine if you supply a valid username in /etc/passwd . However, if you enter an invalid code, it does not do what you might at first expect — it keeps running, and just shows:

Why is this? As mentioned, the $? variable is set to the return code of the last executed command. In this case, that is cut . cut had no problems which it feels like reporting — as far as I can tell from testing it, and reading the documentation, cut returns zero whatever happens! It was fed an empty string, and did its job — returned the first field of its input, which just happened to be the empty string.

So what do we do? If we have an error here, grep will report it, not cut . Therefore, we have to test grep ‘s return code, not cut ‘s.

#!/bin/sh # Second attempt at checking return codes grep "^$:" /etc/passwd > /dev/null 2>&1 if [ "$?" -ne "0" ]; then echo "Sorry, cannot find user $ in /etc/passwd" exit 1 fi USERNAME=`grep "^$:" /etc/passwd|cut -d":" -f1` NAME=`grep "^$:" /etc/passwd|cut -d":" -f5` HOMEDIR=`grep "^$:" /etc/passwd|cut -d":" -f6` echo "USERNAME: $USERNAME" echo "NAME: $NAME" echo "HOMEDIR: $HOMEDIR"

This fixes the problem for us, though at the expense of slightly longer code.
That is the basic way which textbooks might show you, but it is far from being all there is to know about error-checking in shell scripts. This method may not be the most suitable to your particular command-sequence, or may be unmaintainable. Below, we shall investigate two alternative approaches.

Читайте также:  Linux list files permissions

As a second approach, we can tidy this somewhat by putting the test into a separate function, instead of littering the code with lots of 4-line tests:

#!/bin/sh # A Tidier approach check_errs() < # Function. Parameter 1 is the return code # Para. 2 is text to display on failure. if [ "$" -ne "0" ]; then echo "ERROR # $ : $" # as a bonus, make our script exit with the right error code. exit $ fi > ### main script starts here ### grep "^$:" /etc/passwd > /dev/null 2>&1 check_errs $? "User $ not found in /etc/passwd" USERNAME=`grep "^$:" /etc/passwd|cut -d":" -f1` check_errs $? "Cut returned an error" echo "USERNAME: $USERNAME" check_errs $? "echo returned an error - very strange!"

This allows us to test for errors 3 times, with customised error messages, without having to write 3 individual tests. By writing the test routine once. we can call it as many times as we wish, creating a more intelligent script, at very little expense to the programmer. Perl programmers will recognise this as being similar to the die command in Perl.

As a third approach, we shall look at a simpler and cruder method. I tend to use this for building Linux kernels — simple automations which, if they go well, should just get on with it, but when things go wrong, tend to require the operator to do something intelligent (ie, that which a script cannot do!):

#!/bin/sh cd /usr/src/linux && \ make dep && make bzImage && make modules && make modules_install && \ cp arch/i386/boot/bzImage /boot/my-new-kernel && cp System.map /boot && \ echo "Your new kernel awaits, m'lord."

This script runs through the various tasks involved in building a Linux kernel (which can take quite a while), and uses the && operator to check for success. To do this with if would involve:

#!/bin/sh cd /usr/src/linux if [ "$?" -eq "0" ]; then make dep if [ "$?" -eq "0" ]; then make bzImage if [ "$?" -eq "0" ]; then make modules if [ "$?" -eq "0" ]; then make modules_install if [ "$?" -eq "0" ]; then cp arch/i386/boot/bzImage /boot/my-new-kernel if [ "$?" -eq "0" ]; then cp System.map /boot/ if [ "$?" -eq "0" ]; then echo "Your new kernel awaits, m'lord." fi fi fi fi fi fi fi fi

The && and || operators are the shell’s equivalent of AND and OR tests. These can be thrown together as above, or:

#!/bin/sh cp /foo /bar && echo Success || echo Failed

This code will either echo

depending on whether or not the cp command was successful. Look carefully at this; the construct is

command && command-to-execute-on-success || command-to-execute-on-failure

Only one command can be in each part. This method is handy for simple success / fail scenarios, but if you want to check on the status of the echo commands themselves, it is easy to quickly become confused about which && and || applies to which command. It is also very difficult to maintain. Therefore this construct is only recommended for simple sequencing of commands.

Читайте также:  Linux kernel loaded drivers

In earlier versions, I had suggested that you can use a subshell to execute multiple commands depending on whether the cp command succeeded or failed:

cp /foo /bar && ( echo Success ; echo Success part II; ) || ( echo Failed ; echo Failed part II )

But in fact, Marcel found that this does not work properly. The syntax for a subshell is:

( command1 ; command2; command3 )

The return code of the subshell is the return code of the final command ( command3 in this example). That return code will affect the overall command. So the output of this script:

cp /foo /bar && ( echo Success ; echo Success part II; /bin/false ) || ( echo Failed ; echo Failed part II )

Is that it runs the Success part (because cp succeeded, and then — because /bin/false returns failure, it also executes the Failure part:

Success Success part II Failed Failed part II

So if you need to execute multiple commands as a result of the status of some other condition, it is better (and much clearer) to use the standard if , then , else syntax.

Источник

How to use Linux shell command exit codes

You can use the numeric codes returned by shell scripts or Ansible playbooks to identify problems and test the code.

Exit codes

When you execute a command in Linux, it generates a numeric return code. This happens whether you’re running the command directly from the shell, from a script, or even from an Ansible playbook. You can use those return codes to handle the result of that command properly.

What the return codes mean

When running commands at a shell prompt, the special variable $? contains a number that indicates the result of the last command executed.

A zero ( 0 ) means everything went fine. Anything else means there is a problem.

A value of 1 generically indicates that some error has happened.

$ who admin2 :1 2022-03-15 10:14 (:1) $ echo $? 0 $ who | grep thisstringdoesnotexist $ echo $? 1

In the example above, I executed the who command, which showed that I am admin2.

Immediately after that, I executed echo $? , and it returned zero because the previous command was executed successfully.

Then I executed the same who command (which I know is working fine) and piped that to grep with the non-existing argument. This time the $? variable contains a 1 .

Why? It’s because the last command executed was grep , which returns 1 when it cannot find the argument in its input.

$ls myfile.cfg ls: cannot access 'myfile.cfg': No such file or directory $echo $? 2

Here I tried to list a file that doesn’t exist. Then when I entered echo $? I got 2 , which is how the ls command indicates that the argument is not a file or directory name.

Customize the return code

You can also use the exit command from a shell script to customize the return code to the caller script.

The following script illustrates this:

#!/bin/bash if [ ! -f myfile.cfg ]; then echo The file does not exist and we display an error exit 64 fi echo The file exists and we will do something echo "(Not really doing anything, but this is where we could do it)" exit 0
$./myscrypt.sh The file does not exist and we display an error $echo $? 64 $touch myfile.cfg $./myscrypt.sh The file exists and we will do something (Not really doing anything, but this is where we could do it) $echo $? 0

Training & certification

In this script, I explicitly provide the exit code for the failed and for the successful cases. Some observations:

  1. If I do not explicitly use exit 0 , the return code from myscript.sh will be the return code from the last command executed inside it. This could be what I want, but here I wanted to state the return code pretty clearly inside the script.
  2. I used 64 as an arbitrary return code for the error condition. The Advanced Bash-Scripting Guide offers some guidelines about exit code values.
Читайте также:  Форматировать флешку через терминал линукс

Test the return code with a shell script

If you need to test the return code of a command you invoked on your shell script, you just need to test the $? variable immediately after the command executes.

#!/bin/bash # A snipet from a shell script . # Next we will invoke a command or another shell script ./myscript.sh RETURN=$? if [ $RETURN -eq 0 ]; then echo "The script myscript.sh was executed successfuly" exit 0 else echo "The script myscript.sh was NOT executed successfuly and returned the code $RETURN" exit $RETURN fi 

In this example, after invoking my command or script, I saved the exit code from $? on a variable for further utilization. (Again, $? returns the status of the last command, so you need to be careful if you run more commands—even a simple echo .).

Test the return code with an Ansible playbook

It is recommended to avoid running shell commands from an Ansible playbook if there is an Ansible module that performs the same action. This is especially because with a module you have better odds of being idempotent.

[ Ready to start automating? Check out this Ansible quick start series. ]

To illustrate how to handle return codes from a shell command, check this simple playbook:

--- - name: Executes a shell script hosts: localhost gather_facts: no tasks: - name: Execute the shell script shell: ./myscript.sh ignore_errors: true register: result - name: Shows the result of executing the script debug: msg: - "Return code. >" - ">"

This is the Ansible playbook executed when a script returns an error:

[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Executes a shell script] ***************************************************************************************** TASK [Execute the shell script] **************************************************************************************** fatal: [localhost]: FAILED! => . ignoring TASK [Shows the result of executing the script] ************************************************************************ ok: [localhost] => < "msg": [ "Return code. 64", [ "The file does not exist and we display an error" ] ] >PLAY RECAP ************************************************************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 

Источник

Оцените статью
Adblock
detector