How Linux Exit Codes Help You Write Robust Scripts


Summary

  • Each Linux command returns an exit code, with 0 indicating success and non-zero values indicating failure.
  • You can use the $? variable to access the exit code and control program flow in scripts.
  • Be cautious of unconventional exit codes that vary between commands, like diff and curl.

Every Linux command will print error messages if it fails, but this information is difficult to rely on. Your scripts should make use of exit codes to be as robust as possible.

What Are Exit Codes?

When you run a Linux command, it may fail. Things can go wrong for various reasons, but you’ll usually see a message explaining why:

A Linux error message from the ls command reading "no such file or directory."

The error message you see appears on STDERR, a different stream from STDOUT, which lets you isolate errors from expected output. As a user, a description of the error should be informative, but scripts and other automated processes need to know about errors more reliably.

Related


What Are stdin, stdout, and stderr on Linux?

Mystified by stdin, stdout and stderr? Learn to use them in your Linux scripts.

Alongside readable error messages, each command also returns an exit code. This is a number between 0 and 255 that indicates whether the command failed or succeeded. By default, the exit coe is hidden, but your shell provides access to this value in the special variable, $?

The ls command returns a 0 exit status on success and a 1 on failure.

Note that a 0 value indicates success and a non-zero value means failure. You can check this value on the command line, interrogate it in a script, and use it to control the flow of your programs.

How Do I Use Exit Codes?

In the example above, ls returned a 0 exit code when it succeeded and a 1 when its argument was not a valid file. This might seem counterintuitive, and it contradicts typical programming languages that treat 0 as a false value and 1 as true. However, this setup allows for a single, universal success value and a wide range of values to represent different error types.

As well as echoing the $? variable to manually check exit status, you can use it with a conditional if statement. If the command returns a 0 exit status, the statement will run the associated then block. Otherwise, if there is one, it will run the else block. For example:

        if command; then echo "command succeeded"; else echo "command failed"; fi

You can use this construct in a shell script, where it’s a little easier to format for readability:

        if command
    then echo "command succeeded"
    else echo "command failed"
fi

This is the equivalent of:

        command

if [ $? -eq 0 ]
    then echo "command succeeded"
    else echo "command failed"
fi

The longer version uses square-bracket syntax to test the value of $?, but it’s usually easier to test the value directly, as in the first case. Remember that $? always represents the exit code from the last command, so be careful. You may be tempted to output the value of $? for debugging, e.g.

        command

echo "command returned exit code:" $?

if [ $? -eq 0 ]
    then echo "command succeeded"
    else echo "command failed"
fi

But, at the point of the condition check, $? will have the value of echo’s exit status, which is almost guaranteed to be 0. To avoid this, either test the command directly in the conditional or store the value of $? in a variable.

Related


How to Use Bash If Statements (With 4 Examples)

The if statement is the easiest way to explore conditional execution in Bash scripts.

What Else Is There to Know About Exit Status?

Exit codes are pretty simple, following the Unix principle, and this is part of their power. But there are some gotchas and exceptional cases you should be aware of.

Exit Codes From Your Shell

Your shell will use exit codes even when the command you type doesn’t run correctly. For example, zsh and bash use 127 if the command is not found and 126 if the command is not executable:

A shell session showing a 127 exit code for a non-existent command and a 126 code for a command that cannot be executed.

Unconventional Exit Codes

Some commands slightly bend the rules of exit status; they’re really only conventions, anyway. For example, diff uses a 0 value to mean “no differences,” a value of 1 to mean “differences,” and a value of 2 to represent an error:

Example exit codes from diff.

This can confuse the meaning of conditionals; for example:

        if diff file1 file2; then
    echo these files are the same!
fi

The curl command is also a bit unintuitive. You might expect curl to report an HTTP error, like a 404 for a non-existent URL, as a non-zero status code, but it doesn’t:

Exit codes from curl showing that http status is not reflected by default.

You can use the -f (–fail) option to cause curl to behave differently, returning an exit code of 22 on failure:

The curl command reporting an HTTP error via an exit code with the -f option.

However, be aware that this is not 100% guaranteed and some HTTP errors, including those requiring authentication, will still return a 0 exit code.

Using Exit Status in a Command Chain

One of the best exit status shortcuts happens without you even needing to think about it. Here’s a simple example:

        ls program && ./program && echo success || echo epic fail

The && and || logical operators let you run more than one command based on the exit status of others. They are similar to the corresponding operators in most programming languages, but they respect the exit code convention. The && operator will run the command on its right only if the command on its left returns a 0 exit status. The || operator will run the command on its right only if the command on its left fails with a non-zero status.

A chain of commands using logic operators to test exit codes.

A very common case occurs when you build software from scratch, using this recipe:

        ./configure && make && make install
    

The three parts to this command do the following:

  • ./configure runs a local script that inspects your environment and generates a Makefile.
  • make runs the make program and builds the software, using the Makefile.
  • make install copies the generated executable to a well-known location like /usr/local/bin.

If any part of this process fails, it will stop immediately without trying the remainder.

The true and false Commands

Your Linux system includes two curious commands: true and false. Each of these does nothing, except for return an appropriate exit code: 0 and 1 respectively:

The true and false commands returning 0 and 1 exit codes respectively.

They might not seem very useful, but these commands come in handy for testing and for some shell scripting tasks. You can use true in a while statement to create an infinite loop:

        while true
do
    echo “running until you ^c”
    sleep 10
done

You can also use them together with logical operators to alter command behavior. For example, you can temporarily stop a long command chain from running:

        false && (none || of-these && commands || will-run)

Or you can force a command chain to continue running, even if one command fails:

        cat file-that-may-not-exist | true && echo done

Returning an Exit Code From Your Own Scripts

You’ve probably used the exit command before, to quit your terminal. Technically, it quits your shell and this means you can use it to stop a shell script too. By default, your scripts will exit with the same code as the last command:

A shell script returning its last command's exit code.

But you can change the exit code by passing a numerical parameter to the exit command:

        exit number

Your script will now exit with the status code you supplied, and you can use this to communicate different error conditions from your program to others.


Once you know about them, exit commands are simple to use. You may even use them without really thinking about it, but when it comes to scripting, make sure you’re using exit codes appropriately.



Source link

Previous articleSTALKER 2 gets an ambitious roadmap update, details within
Next articleDN Miner Revolutionizes Entry-Level Bitcoin Mining with