Bash exit with error message

Is it possible to exit on error, with a message, without using if statements? [[ $TRESHOLD =~ ^[0-9]+$ ]] || exit ERRCODE "Threshold must be an integer value!" Of course the right side of || won'...

Is it possible to exit on error, with a message, without using if statements?

[[ $TRESHOLD =~ ^[0-9]+$ ]] || exit ERRCODE "Threshold must be an integer value!"

Of course the right side of || won’t work, just to give you better idea of what I am trying to accomplish.

Actually, I don’t even mind with which ERR code it’s gonna exit, just to show the message.

EDIT

I know this will work, but how to suppress numeric arg required showing
after my custom message?

[[ $TRESHOLD =~ ^[0-9]+$ ]] || exit "Threshold must be an integer value!"

asked Jul 6, 2014 at 16:15

branquito's user avatar

branquitobranquito

3,7845 gold badges31 silver badges60 bronze badges

0

exit doesn’t take more than one argument. To print any message like you want, you can use echo and then exit.

    [[ $TRESHOLD =~ ^[0-9]+$ ]] || 
     { echo "Threshold must be an integer value!"; exit $ERRCODE; }

answered Jul 6, 2014 at 16:22

P.P's user avatar

P.PP.P

116k20 gold badges171 silver badges231 bronze badges

5

You can use a helper function:

function fail {
    printf '%sn' "$1" >&2 ## Send message to stderr.
    exit "${2-1}" ## Return a code specified by $2, or 1 by default.
}

[[ $TRESHOLD =~ ^[0-9]+$ ]] || fail "Threshold must be an integer value!"

Function name can be different.

answered Jul 6, 2014 at 16:28

konsolebox's user avatar

konsoleboxkonsolebox

70.1k12 gold badges98 silver badges105 bronze badges

1

Using exit directly may be tricky as the script may be sourced from other places. I prefer instead using subshell with set -e (plus errors should go into cerr, not cout) :

set -e
[[ $TRESHOLD =~ ^[0-9]+$ ]] || 
     (>&2 echo "Threshold must be an integer value!"; exit $ERRCODE)

answered Mar 5, 2019 at 15:34

noonex's user avatar

noonexnoonex

1,8861 gold badge16 silver badges17 bronze badges

how about just echo a text and then exit:

echo "No Such File" && exit

answered Jul 29, 2022 at 8:42

vagitus's user avatar

vagitusvagitus

2542 silver badges10 bronze badges

Содержание

  1. Как использовать коды завершения в Bash-скриптах
  2. Что такое коды завершения
  3. Что происходит, когда коды завершения не определены
  4. Как использовать коды завершения в Bash-скриптах
  5. Проверяем коды завершения
  6. Создаём собственный код завершения
  7. Как использовать коды завершения в командной строке
  8. Дополнительные коды завершения
  9. Exit a Bash Script: Exit 0 and Exit 1 Explained
  10. Exit a Bash Script With Error
  11. What Are Exit 0 and Exit 1 in a Bash Script?
  12. More About The Status of the Bash Exit Command
  13. An Example of Script Failure
  14. Bash If Else Applied to $? Variable
  15. Conclusion
  16. How to Exit When Errors Occur in Bash Scripts
  17. Exit When Any Command Fails
  18. Exit Only When Specific Commands Fail
  19. Conclusion
  20. Suggested Articles

Как использовать коды завершения в Bash-скриптах

Инструменты автоматизации и мониторинга удобны тем, что разработчик может взять готовые скрипты, при необходимости адаптировать и использовать в своём проекте. Можно заметить, что в некоторых скриптах используются коды завершения (exit codes), а в других нет. О коде завершения легко забыть, но это очень полезный инструмент. Особенно важно использовать его в скриптах командной строки.

Что такое коды завершения

В Linux и других Unix-подобных операционных системах программы во время завершения могут передавать значение родительскому процессу. Это значение называется кодом завершения или состоянием завершения. В POSIX по соглашению действует стандарт: программа передаёт 0 при успешном исполнении и 1 или большее число при неудачном исполнении.

Почему это важно? Если смотреть на коды завершения в контексте скриптов для командной строки, ответ очевиден. Любой полезный Bash-скрипт неизбежно будет использоваться в других скриптах или его обернут в однострочник Bash. Это особенно актуально при использовании инструментов автоматизации типа SaltStack или инструментов мониторинга типа Nagios. Эти программы исполняют скрипт и проверяют статус завершения, чтобы определить, было ли исполнение успешным.

Кроме того, даже если вы не определяете коды завершения, они всё равно есть в ваших скриптах. Но без корректного определения кодов выхода можно столкнуться с проблемами: ложными сообщениями об успешном исполнении, которые могут повлиять на работу скрипта.

Что происходит, когда коды завершения не определены

В Linux любой код, запущенный в командной строке, имеет код завершения. Если код завершения не определён, Bash-скрипты используют код выхода последней запущенной команды. Чтобы лучше понять суть, обратите внимание на пример.

Этот скрипт запускает команды touch и echo . Если запустить этот скрипт без прав суперпользователя, команда touch не выполнится. В этот момент мы хотели бы получить информацию об ошибке с помощью соответствующего кода завершения. Чтобы проверить код выхода, достаточно ввести в командную строку специальную переменную $? . Она печатает код возврата последней запущенной команды.

Как видно, после запуска команды ./tmp.sh получаем код завершения 0 . Этот код говорит об успешном выполнении команды, хотя на самом деле команда не выполнилась. Скрипт из примера выше исполняет две команды: touch и echo . Поскольку код завершения не определён, получаем код выхода последней запущенной команды. Это команда echo , которая успешно выполнилась.

Если убрать из скрипта команду echo , можно получить код завершения команды touch .

Поскольку touch в данном случае — последняя запущенная команда, и она не выполнилась, получаем код возврата 1 .

Как использовать коды завершения в Bash-скриптах

Удаление из скрипта команды echo позволило нам получить код завершения. Что делать, если нужно сделать разные действия в случае успешного и неуспешного выполнения команды touch ? Речь идёт о печати stdout в случае успеха и stderr в случае неуспеха.

Проверяем коды завершения

Выше мы пользовались специальной переменной $? , чтобы получить код завершения скрипта. Также с помощью этой переменной можно проверить, выполнилась ли команда touch успешно.

После рефакторинга скрипта получаем такое поведение:

  • Если команда touch выполняется с кодом 0 , скрипт с помощью echo сообщает об успешно созданном файле.
  • Если команда touch выполняется с другим кодом, скрипт сообщает, что не смог создать файл.

Любой код завершения кроме 0 значит неудачную попытку создать файл. Скрипт с помощью echo отправляет сообщение о неудаче в stderr .

Создаём собственный код завершения

Наш скрипт уже сообщает об ошибке, если команда touch выполняется с ошибкой. Но в случае успешного выполнения команды мы всё также получаем код 0 .

Поскольку скрипт завершился с ошибкой, было бы не очень хорошей идеей передавать код успешного завершения в другую программу, которая использует этот скрипт. Чтобы добавить собственный код завершения, можно воспользоваться командой exit .

Теперь в случае успешного выполнения команды touch скрипт с помощью echo сообщает об успехе и завершается с кодом 0 . В противном случае скрипт печатает сообщение об ошибке при попытке создать файл и завершается с кодом 1 .

Как использовать коды завершения в командной строке

Скрипт уже умеет сообщать пользователям и программам об успешном или неуспешном выполнении. Теперь его можно использовать с другими инструментами администрирования или однострочниками командной строки.

В примере выше && используется для обозначения «и», а || для обозначения «или». В данном случае команда выполняет скрипт ./tmp.sh , а затем выполняет echo «bam» , если код завершения 0 . Если код завершения 1 , выполняется следующая команда в круглых скобках. Как видно, в скобках для группировки команд снова используются && и || .

Скрипт использует коды завершения, чтобы понять, была ли команда успешно выполнена. Если коды завершения используются некорректно, пользователь скрипта может получить неожиданные результаты при неудачном выполнении команды.

Дополнительные коды завершения

Команда exit принимает числа от 0 до 255 . В большинстве случаев можно обойтись кодами 0 и 1 . Однако есть зарезервированные коды, которые обозначают конкретные ошибки. Список зарезервированных кодов можно посмотреть в документации.

Адаптированный перевод статьи Understanding Exit Codes and how to use them in bash scripts by Benjamin Cane. Мнение администрации Хекслета может не совпадать с мнением автора оригинальной публикации.

Источник

Exit a Bash Script: Exit 0 and Exit 1 Explained

Why would you exit from a Bash script?

Let’s say you are writing a script…there’s one thing that is almost certain…

At some point, your script is likely to break because of unexpected conditions you might have not considered.

So, what can you do about it?

How can you exit a Bash script in case of errors?

Bash provides a command to exit a script if errors occur, the exit command. The argument N (exit status) can be passed to the exit command to indicate if a script is executed successfully (N = 0) or unsuccessfully (N != 0). If N is omitted the exit command takes the exit status of the last command executed.

In this guide you will learn how to use the exit command in your scripts to make them more robust and to provide a great experience to those who are using your scripts.

Let’s get started.

Table of Contents

Exit a Bash Script With Error

What can you do to write robust scripts that don’t break in unexpected ways in case of errors?

The answer to this question is: don’t forget error handling.

Instead of “hoping” that nothing will go wrong with your program, you can predict possible failures and decide how your program will react to those.

How will you handle a failure if it occurs? What will the user see?

We will have a look at how error handling works in Bash scripting (or Shell scripting in general).

These concepts apply to all programming languages even if the way error handling is implemented varies between programming languages.

To handle errors in Bash we will use the exit command, whose syntax is:

Where N is the Bash exit code (or exit status) used to exit the script during its execution.

Different values of N are used to indicate if the script exits with success or failure.

But, why different exit codes depending on the success or failure of a script?

Because often other programs will be calling your script and they will need to be able to understand if everything goes well with the execution of your script or not as part of their error handling.

Let’s have a look at what exit 1 means.

What Are Exit 0 and Exit 1 in a Bash Script?

How do you exit a Bash script on error?

The standard convention is the following:

Bash exit code Meaning
Zero (0) Success
Non-zero (1, 2, 3, etc…) Failure

As you can see from the table above, failures can be represented with any non-zero exit codes.

For instance:

  • 1 may be used if incorrect arguments are passed to the script
  • 2 if the script cannot find a file it needs
  • 3 if the file it needs has an incorrect format
  • And so on…

You can be creative and use non-zero Bash exit codes to be very clear about the reason why your script has failed.

So, going back to the exit command.

What happens when it gets executed?

The exit command returns an exit code back to the shell.

Here’s an example…

Let’s write a script called exit_status.sh:

So, here we are passing the exit code 0 to the exit command.

How can we verify that this is actually the code passed by the script back to the shell?

We use the $? variable. Remember it, this is very important!

$? is a variable that contains the exit code of the last command executed.

And how can we read its value?

Using the echo command, in the same way we print the value of any variable:

Voilà, here is the 0 exit code.

And what happens if we remove “exit 0” from the script?

Run the script again and check the value of $?:

We still get the 0 exit code back…

So, nothing changed…why?

Because every command we execute, including the echo command (the only command in our script) returns an exit code.

And in this case the echo command executed as part of the script is returning a 0 exit code because the echo “Exit command test” command is executed successfully.

More About The Status of the Bash Exit Command

I will show you an example of how the exit code is applied to the execution of any commands.

This is really important in your Bash knowledge and I want to make sure it’s clear to you.

Here is an example with the cat command (this applies to all commands)…

I open a shell on my computer and in the current directory I have the following files:

If I use the cat command to see the content of the file test_file1 the exit code stored in the variable $? after the execution of the cat command is 0 because the execution of the cat command is successful:

If by mistake I try to print the content of the file test_file3 (that doesn’t exist), the value of the variable $? is not 0.

In this case it’s 1, but it can have other values different than zero. This is useful to represent multiple types of errors for a given command:

If you have any questions please let me know in the comments below.

An Example of Script Failure

Now let’s modify the echo command in the simple script we have used before.

We want run it with an incorrect syntax and see if it exits with a non-zero exit code.

Remove the double quotes at the end of the echo command as shown below:

If we execute the script we see the message “syntax error: unexpected end of file“:

And the exit code is 2. So a non-zero exit code as we expected.

As mentioned before, remember the $? variable because it makes a big difference in the way you handle errors and you make your scripts robust.

The Bash if else statement can also be used to write more complex logic that checks the value of the $? variable and takes different actions based on that.

Bash If Else Applied to $? Variable

Let’s see how you can use a Bash if else statement together with the $? variable.

The following script tries to create a subdirectory tmp/project in the current directory.

In the condition of the if statement we verify if the value of the variable $? is different than 0. If that’s the case we print an error message and exit the script with exit code 1.

The else branch prints a successful message and it’s executed only if the value of $? is 0.

Let’s run the script:

The mkdir command fails because the tmp directory doesn’t exist and as expected the exit status of the script is 1.

I want to see what happens if I update the mkdir command in the script to include the -p flag that creates the tmp directory if it doesn’t exist:

And here is the output of the script:

This time the directory gets created successfully and the exit code returned by the script is zero as expected (the else branch of the if else statement gets executed).

Conclusion

We went through quite a lot!

Now you know:

  • Why exit 0 and exit 1 are used in Bash scripts.
  • How you can use the variable $? to read the exit code returned by a command (or script)
  • The way to use a Bash if else statement together with the $? variable.

And now it’s your turn…

What kind of failures do you want to handle in your script?

Let me know in the comments! 😀

Related FREE Course: Decipher Bash Scripting

Источник

How to Exit When Errors Occur in Bash Scripts

By Evan Sangaline | September 11, 2017

It’s a common issue that scripts written and tested on GNU/Linux don’t run correctly on macOS–or vice versa–because of differences between the GNU and BSD versions of the core utils. Error messages can get drowned in the script output, making it far from obvious that something isn’t executing correctly. There are a couple of easy fixes to avoid problems like this, but they rely on some bash features that you may not be familiar with if you don’t do a ton of scripting. I’ll summarize my two approaches here and hopefully they’re of some use to you if you’re looking for a how-to guide for this specific problem.

Exit When Any Command Fails

This can actually be done with a single line using the set builtin command with the -e option.

Putting this at the top of a bash script will cause the script to exit if any commands return a non-zero exit code. We can get a little fancier if we use DEBUG and EXIT traps to execute custom commands before each line of the script is run and before the script exits, respectively. Adding the following lines will allow us to print out a nice error message including the previously run command and its exit code.

For example, if we run ls —fake-option then we’ll see an informative error message as follows.

Exit Only When Specific Commands Fail

The global solution is often fine, but sometimes you only care if certain commands fail. We can handle this situation by defining a function that needs to be explicitly invoked to check the status code and exit if necessary.

That bottom line isn’t strictly necessary, but it allows us to use !! and have it expand to the last command executed. For example, we can check explicitly for an error like this

will pass the exit code of the previous command as the first argument to exit_on_error() and then !! will expand to ls —fake-option as the second and third arguments. The second and third arguments–plus any further arguments if they were there–are then recombined by slicing $ <@:2>.

This approach will print the same error message as the one in the previous section, but will only check the commands that are explicitly followed by exit_on_error calls.

Conclusion

Hopefully you found one of these two methods helpful! If you’re ever struggling with any devops or infrastructure issues then please reach out about our consulting services in these areas.

Suggested Articles

If you enjoyed this article, then you might also enjoy these related ones.

Источник

When writing Bash scripts, it’s essential to include error handling to ensure that the script exits gracefully in the event of an error. In this article, we’ll cover all the possible techniques for exiting when errors occur in Bash scripts, including the use of exit codes, the “set -e” option, the “trap” command, and command substitution. In this article, we will discuss the various techniques and methods to handle errors and exit gracefully in Bash scripts. Error handling is an essential aspect of writing scripts, as it helps to ensure that the script exits in an appropriate manner, even when an error occurs. Without proper error handling, a script might continue to execute even when an error has occurred, leading to unexpected results or even system crashes.

We will start by discussing the use of exit codes, which are used to indicate the status of the script’s execution. We will then move on to the “set -e” option, which causes the script to exit immediately if any command exits with a non-zero exit code. Next, we will cover the “trap” command, which allows you to specify a command or set of commands to be executed when the script exits, whether it’s due to an error or not. Then, we will look at command substitution, which allows you to capture the output of a command and assign it to a variable. Finally, we will discuss the technique of suppressing error messages.

Exiting when errors occur in Bash scripts is an important aspect of script development, as it helps to ensure that the script stops running and exits cleanly in the event of an error. There are several ways to exit a Bash script when an error occurs, including using the exit command, using the return command, and using the trap command. It is also important to include error handling and error messages in your script to provide useful information to the user in the event of an error.

Exit Codes

Exit codes are used to indicate the status of the script’s execution, with a zero exit code indicating success and non-zero codes indicating an error. One of the simplest ways to exit a Bash script when an error occurs is to use the “exit” command with a non-zero exit code. For example, the following script exits with a code of 1 if the “mkdir” command fails:

Script:

#!/bin/bash
mkdir /tmp/example
if [ $? -ne 0 ]; then
  exit 1
fi

Output:

Exit codes

Another way to check the exit code of a command is by using the $? variable. For example, the following script also exits with a code of 1 if the “mkdir” command fails:

Script:

#!/bin/bash

mkdir /home/lucifer/yash/first

if [ $? -ne 0 ]; then

  echo “Error: Failed to create directory”

  exit 1

fi

Output:

Exit Codes

Set -e Option

Another way to exit a Bash script when an error occurs is to use the “set -e” option. This option causes the script to exit immediately if any command exits with a non-zero exit code. For example, the following script exits immediately if the “mkdir” command fails:

Script:

#!/bin/bash
set -e
mkdir /tmp/example

Output:

Using -e option

Trap Command

The trap command allows you to specify a command or set of commands to be executed when the script exits, whether it’s due to an error or not. For example, the following script uses the trap command to delete the directory if the script exits with a non-zero exit code:

Script:

#!/bin/bash
trap 'rm -rf /tmp/example' EXIT
mkdir /tmp/example
# some commands here
exit 0

Output:

Using trap command

Command Substitution

Another way to handle errors in Bash scripts is by using command substitution. This technique allows you to capture the output of a command and assign it to a variable. For example, the following script captures the output of the “mkdir” command and assigns it to the “result” variable. If the command fails, the script exits with a code of 1:

Script:

#!/bin/bash
result=$(mkdir /tmp/example)
if [ $? -ne 0 ]; then
  echo "Error: $result"
  exit 1
fi

Output:

Command Substitution

In this example, if the mkdir command fails to create the directory, the error message will be assigned to the “result” variable and displayed on the console.

Suppressing Error Messages

It is also possible to suppress the error message generated by a command by adding 2> /dev/null to the command.

Script:

#!/bin/bash
mkdir /tmp/example 2> /dev/null
if [ $? -ne 0 ]; then
  exit 1
fi

Output:

Suppressing Error Messages

In this example, if the mkdir command fails to create the directory, the error message will not be displayed on the console.

Exit When Any Command Fails

One way to exit a Bash script when any command fails is to use the set -e option. This option causes the script to exit immediately if any command returns a non-zero exit status.
For example, the following script will exit if the ls command fails to list the contents of the specified directory:

Script:

#!/bin/bash
set -e
ls /nonexistent_directory
echo "This line will not be executed"

If the directory /nonexistent_directory does not exist, the script will exit with an error message like this:

Output:

Exiting when command fails

And it will not execute the second line.

Another way to exit if any command fails is to use the || operator after each command, for example:

Script:

#!/bin/bash

ls /nonexistent_directory || exit 1

echo “This line will not be executed”This script will also exit with an error message

Output:

Exiting when command fails

And it will not execute the second line.

You can also check the exit status of the last command with $? variable and use the if condition with the exit command to exit if any command fails.

Script:

#!/bin/bash
ls /nonexistent_directory
if [ $? -ne 0 ]
then
  echo "Command failed"
  exit 1
else
  echo "Command Successful"
fi

This script will also exit with an error message:

Output:

Exiting when command fails

And it will not execute the second line.

It is important to note that, when using the set -e option or || operator, the script will exit even if the failure is in a command inside a function or a subshell. To prevent this behavior, you can use set +e before the function or subshell and set -e after it.

Exit Only When Specific Commands Fail

To exit a Bash script only when specific commands fail, you can use the || operator in conjunction with the exit command. The || operator is used to execute a command only if the preceding command fails (returns a non-zero exit status).
For example, the following script will exit if the ls command fails to list the contents of the specified directory, but will continue to execute subsequent commands even if they fail:

Script:

#!/bin/bash
ls /nonexistent_directory || exit 1
echo "This line will be executed"
rm /nonexistent_file || true
echo "This line will also be executed"

If the directory /nonexistent_directory does not exist, the script will exit with an error message like this:

Output:

Exiting when specific command fails

And it will not execute the second and third lines.

It is important to note that, the true command is used to prevent the rm command from causing the script to exit if it fails. Without the true command, the script would exit if the file /nonexistent_file does not exist.

Another way to achieve this is by using an if statement and checking the exit status of the command with $? variable, like:

Script:

#!/bin/bash
ls /nonexistent_directory
if [ $? -ne 0 ]
then
  echo "Command failed"
  exit 1
else
  echo "Command Successful"
fi
echo "This line will be executed"
rm /nonexistent_file
if [ $? -ne 0 ]
then
  echo "Command failed"
else
  echo "Command Successful"
fi

This script will also exit with an error message:

Output:

Exiting when specific command fails

And it will not execute the second and third lines.

It is important to note that, the exit status of the last executed command is stored in the $? variable. And you can use it to check if the command is successful or not.

Conclusion

There are multiple ways to handle errors and exit gracefully in Bash scripts. By using exit codes, the “set -e” option, the “trap” command, command substitution, and suppressing error messages, you can ensure that your script exits in an appropriate manner and provides appropriate error handling for your script. It’s essential to test your script with different inputs and error conditions to ensure that it is handling errors as expected. It’s also important to include clear and descriptive error messages that indicate the cause of the error and provide guidance on how to resolve it. With these techniques, you can write robust and reliable Bash scripts that can handle errors and exit gracefully.

Why would you exit from a Bash script?

Let’s say you are writing a script…there’s one thing that is almost certain…

At some point, your script is likely to break because of unexpected conditions you might have not considered.

So, what can you do about it?

How can you exit a Bash script in case of errors?

Bash provides a command to exit a script if errors occur, the exit command. The argument N (exit status) can be passed to the exit command to indicate if a script is executed successfully (N = 0) or unsuccessfully (N != 0). If N is omitted the exit command takes the exit status of the last command executed.

In this guide you will learn how to use the exit command in your scripts to make them more robust and to provide a great experience to those who are using your scripts.

Let’s get started.

Exit a Bash Script With Error

What can you do to write robust scripts that don’t break in unexpected ways in case of errors?

The answer to this question is: don’t forget error handling.

Instead of “hoping” that nothing will go wrong with your program, you can predict possible failures and decide how your program will react to those.

How will you handle a failure if it occurs? What will the user see?

We will have a look at how error handling works in Bash scripting (or Shell scripting in general).

These concepts apply to all programming languages even if the way error handling is implemented varies between programming languages.

To handle errors in Bash we will use the exit command, whose syntax is:

exit N

Where N is the Bash exit code (or exit status) used to exit the script during its execution.

Different values of N are used to indicate if the script exits with success or failure.

But, why different exit codes depending on the success or failure of a script?

Because often other programs will be calling your script and they will need to be able to understand if everything goes well with the execution of your script or not as part of their error handling.

Let’s have a look at what exit 1 means.

What Are Exit 0 and Exit 1 in a Bash Script?

How do you exit a Bash script on error?

The standard convention is the following:

Bash exit code Meaning
Zero (0) Success
Non-zero (1, 2, 3, etc…) Failure

As you can see from the table above, failures can be represented with any non-zero exit codes.

For instance:

  • 1 may be used if incorrect arguments are passed to the script
  • 2 if the script cannot find a file it needs
  • 3 if the file it needs has an incorrect format
  • And so on…

You can be creative and use non-zero Bash exit codes to be very clear about the reason why your script has failed.

So, going back to the exit command.

What happens when it gets executed?

The exit command returns an exit code back to the shell.

Here’s an example…

Let’s write a script called exit_status.sh:

#!/bin/bash

echo "Exit command test"
exit 0 

So, here we are passing the exit code 0 to the exit command.

How can we verify that this is actually the code passed by the script back to the shell?

We use the $? variable. Remember it, this is very important!

$? is a variable that contains the exit code of the last command executed.

And how can we read its value?

Using the echo command, in the same way we print the value of any variable:

(localhost)$ ./exit_status.sh 
Exit command test
(localhost)$ echo $?
0

Voilà, here is the 0 exit code.

And what happens if we remove “exit 0” from the script?

#!/bin/bash

echo "Exit command test"

Run the script again and check the value of $?:

(localhost)$ ./exit_status.sh 
Exit command test
(localhost)$ echo $?
0

We still get the 0 exit code back…

So, nothing changed…why?

Because every command we execute, including the echo command (the only command in our script) returns an exit code.

And in this case the echo command executed as part of the script is returning a 0 exit code because the echo “Exit command test” command is executed successfully.

More About The Status of the Bash Exit Command

I will show you an example of how the exit code is applied to the execution of any commands.

This is really important in your Bash knowledge and I want to make sure it’s clear to you.

Here is an example with the cat command (this applies to all commands)…

I open a shell on my computer and in the current directory I have the following files:

(localhost)$ ls -al
total 16
drwxr-xr-x   4 myuser  mygroup  128  4 Jul 12:38 .
drwxr-xr-x  11 myuser  mygroup  352  4 Jul 12:38 ..
-rw-r--r--   1 myuser  mygroup   10  4 Jul 12:38 test_file1
-rw-r--r--   1 myuser  mygroup   10  4 Jul 12:38 test_file2

If I use the cat command to see the content of the file test_file1 the exit code stored in the variable $? after the execution of the cat command is 0 because the execution of the cat command is successful:

(localhost)$ cat test_file1 
Test file
(localhost)$ echo $?
0

If by mistake I try to print the content of the file test_file3 (that doesn’t exist), the value of the variable $? is not 0.

In this case it’s 1, but it can have other values different than zero. This is useful to represent multiple types of errors for a given command:

(localhost)$ cat test_file3
cat: test_file3: No such file or directory
(localhost)$ echo $?
1

All clear?

If you have any questions please let me know in the comments below.

An Example of Script Failure

Now let’s modify the echo command in the simple script we have used before.

We want run it with an incorrect syntax and see if it exits with a non-zero exit code.

Remove the double quotes at the end of the echo command as shown below:

#!/bin/bash

echo "Exit command test

If we execute the script we see the message “syntax error: unexpected end of file“:

(localhost)$ ./exit_status.sh 
./exit_status.sh: line 3: unexpected EOF while looking for matching `"'
./exit_status.sh: line 4: syntax error: unexpected end of file
(localhost)$ echo $?
2

And the exit code is 2. So a non-zero exit code as we expected.

As mentioned before, remember the $? variable because it makes a big difference in the way you handle errors and you make your scripts robust.

The Bash if else statement can also be used to write more complex logic that checks the value of the $? variable and takes different actions based on that.

Bash If Else Applied to $? Variable

Let’s see how you can use a Bash if else statement together with the $? variable.

The following script tries to create a subdirectory tmp/project in the current directory.

In the condition of the if statement we verify if the value of the variable $? is different than 0. If that’s the case we print an error message and exit the script with exit code 1.

The else branch prints a successful message and it’s executed only if the value of $? is 0.

#!/bin/bash
  
mkdir tmp/project

if [[ $? -ne 0 ]] ; then
    echo "Unable to create directory tmp/project"
    exit 1
else
    echo "Directory tmp/project created successfully"
fi

Let’s run the script:

(localhost)$ ./exit.sh 
mkdir: tmp: No such file or directory
Unable to create directory tmp/project
(localhost)$ echo $?
1

The mkdir command fails because the tmp directory doesn’t exist and as expected the exit status of the script is 1.

I want to see what happens if I update the mkdir command in the script to include the -p flag that creates the tmp directory if it doesn’t exist:

mkdir -p tmp/project

And here is the output of the script:

(localhost)$ ./exit.sh 
Directory tmp/project created successfully
(localhost)$ echo $?
0

This time the directory gets created successfully and the exit code returned by the script is zero as expected (the else branch of the if else statement gets executed).

Conclusion

We went through quite a lot!

Now you know:

  • Why exit 0 and exit 1 are used in Bash scripts.
  • How you can use the variable $? to read the exit code returned by a command (or script)
  • The way to use a Bash if else statement together with the $? variable.

And now it’s your turn…

What kind of failures do you want to handle in your script?

Let me know in the comments! 😀


Related FREE Course: Decipher Bash Scripting

Related posts:

I’m a Tech Lead, Software Engineer and Programming Coach. I want to help you in your journey to become a Super Developer!

Contents

  • 1 Problem
  • 2 Solutions
    • 2.1 Executed in subshell, exit on error
    • 2.2 Executed in subshell, trap error
  • 3 Caveat 1: `Exit on error’ ignoring subshell exit status
    • 3.1 Solution: Generate error yourself if subshell fails
      • 3.1.1 Example 1
      • 3.1.2 Example 2
  • 4 Caveat 2: `Exit on error’ not exitting subshell on error
    • 4.1 Solution: Use logical operators (&&, ||) within subshell
      • 4.1.1 Example
  • 5 Caveat 3: `Exit on error’ not exitting command substition on error
    • 5.1 Solution 1: Use logical operators (&&, ||) within command substitution
    • 5.2 Solution 2: Enable posix mode
  • 6 The tools
    • 6.1 Exit on error
      • 6.1.1 Specify `bash -e’ as the shebang interpreter
        • 6.1.1.1 Example
      • 6.1.2 Set ERR trap to exit
        • 6.1.2.1 Example
  • 7 Solutions revisited: Combining the tools
    • 7.1 Executed in subshell, trap on exit
      • 7.1.1 Rationale
    • 7.2 Sourced in current shell
      • 7.2.1 Todo
      • 7.2.2 Rationale
        • 7.2.2.1 `Exit’ trap in sourced script
        • 7.2.2.2 `Break’ trap in sourced script
        • 7.2.2.3 Trap in function in sourced script without `errtrace’
        • 7.2.2.4 Trap in function in sourced script with ‘errtrace’
        • 7.2.2.5 `Break’ trap in function in sourced script with `errtrace’
  • 8 Test
  • 9 See also
  • 10 Journal
    • 10.1 20210114
    • 10.2 20060524
    • 10.3 20060525
  • 11 Comments

Problem

I want to catch errors in bash script using set -e (or set -o errexit or trap ERR). What are best practices?

Solutions

See #Solutions revisited: Combining the tools for detailed explanations.

If the script is executed in a subshell, it’s relative easy: You don’t have to worry about backing up and restoring shell options and shell traps, because they’re automatically restored when you exit the subshell.

Executed in subshell, exit on error

Example script:

#!/bin/bash -eu
# -e: Exit immediately if a command exits with a non-zero status.
# -u: Treat unset variables as an error when substituting.
 
(false)                   # Caveat 1: If an error occurs in a subshell, it isn't detected
(false) || false          # Solution: If you want to exit, you have to detect the error yourself
(false; true) || false    # Caveat 2: The return status of the ';' separated list is `true'
(false && true) || false  # Solution: If you want to control the last command executed, use `&&'

See also #Caveat 1: `Exit on error’ ignoring subshell exit status

Executed in subshell, trap error

#!/bin/bash -Eu
# -E: ERR trap is inherited by shell functions.
# -u: Treat unset variables as an error when substituting.
# 
# Example script for handling bash errors.  Exit on error.  Trap exit.
# This script is supposed to run in a subshell.
# See also: http://fvue.nl/wiki/Bash:_Error_handling

    #  Trap non-normal exit signals: 1/HUP, 2/INT, 3/QUIT, 15/TERM, ERR
trap onexit 1 2 3 15 ERR


#--- onexit() -----------------------------------------------------
#  @param $1 integer  (optional) Exit status.  If not set, use `$?'

function onexit() {
    local exit_status=${1:-$?}
    echo Exiting $0 with $exit_status
    exit $exit_status
}


# myscript


    # Allways call `onexit' at end of script
onexit

Caveat 1: `Exit on error’ ignoring subshell exit status

The `-e’ setting does not exit if an error occurs within a subshell, for example with these subshell commands: (false) or bash -c false

Example script caveat1.sh:

#!/bin/bash -e
echo begin
(false)
echo end

Executing the script above gives:

$ ./caveat1.sh
begin
end
$

Conclusion: the script didn’t exit after (false).

Solution: Generate error yourself if subshell fails

( SHELL COMMANDS ) || false

In the line above, the exit status of the subshell is checked. The subshell must exit with a zero status — indicating success, otherwise `false’ will run, generating an error in the current shell.

Note that within a bash `list’, with commands separated by a `;’, the return status is the exit status of the last command executed. Use the control operators `&&’ and `||’ if you want to control the last command executed:

$ (false; true) || echo foo
$ (false && true) || echo foo
foo
$

Example 1

Example script example.sh:

#!/bin/bash -e
echo begin
(false) || false
echo end

Executing the script above gives:

$ ./example.sh
begin
$

Conclusion: the script exits after false.

Example 2

Example bash commands:

$ trap 'echo error' ERR       # Set ERR trap
$ false                       # Non-zero exit status will be trapped
error
$ (false)                     # Non-zero exit status within subshell will not be trapped
$ (false) || false            # Solution: generate error yourself if subshell fails
error
$ trap - ERR                  # Reset ERR trap

Caveat 2: `Exit on error’ not exitting subshell on error

The `-e’ setting doesn’t always immediately exit the subshell `(…)’ when an error occurs. It appears the subshell behaves as a simple command and has the same restrictions as `-e’:

Exit immediately if a simple command exits with a non-zero status, unless the subshell is part of the command list immediately following a `while’ or `until’ keyword, part of the test in an `if’ statement, part of the right-hand-side of a `&&’ or `||’ list, or if the command’s return status is being inverted using `!’

Example script caveat2.sh:

#!/bin/bash -e
(false; echo A)                        # Subshell exits after `false'
!(false; echo B)                       # Subshell doesn't exit after `false'
true && (false; echo C)                # Subshell exits after `false'
(false; echo D) && true                # Subshell doesn't exit after `false'
(false; echo E) || false               # Subshell doesn't exit after `false'
if (false; echo F); then true; fi      # Subshell doesn't exit after `false'
while (false; echo G); do break; done  # Subshell doesn't exit after `false'
until (false; echo H); do break; done  # Subshell doesn't exit after `false'

Executing the script above gives:

$ ./caveat2.sh
B
D
E
F
G
H

Solution: Use logical operators (&&, ||) within subshell

Use logical operators `&&’ or `||’ to control execution of commands within a subshell.

Example

#!/bin/bash -e
(false && echo A)
!(false && echo B)
true && (false && echo C)
(false && echo D) && true
(false && echo E) || false
if (false && echo F); then true; fi
while (false && echo G); do break; done
until (false && echo H); do break; done

Executing the script above gives no output:

$ ./example.sh
$

Conclusion: the subshells do not output anything because the `&&’ operator is used instead of the command separator `;’ as in caveat2.sh.

Caveat 3: `Exit on error’ not exitting command substition on error

The `-e’ setting doesn’t immediately exit command substitution when an error occurs, except when bash is in posix mode:

$ set -e
$ echo $(false; echo A)
A

Solution 1: Use logical operators (&&, ||) within command substitution

$ set -e
$ echo $(false || echo A)

Solution 2: Enable posix mode

When posix mode is enabled via set -o posix, command substition will exit if `-e’ has been set in the
parent shell.

$ set -e
$ set -o posix
$ echo $(false; echo A)

Enabling posix might have other effects though?

The tools

Exit on error

Bash can be told to exit immediately if a command fails. From the bash manual («set -e»):

«Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a && or || list, or if the command’s return value is being inverted via !. A trap on ERR, if set, is executed before the shell exits.»

To let bash exit on error, different notations can be used:

  1. Specify `bash -e’ as shebang interpreter
  2. Start shell script with `bash -e’
  3. Use `set -e’ in shell script
  4. Use `set -o errexit’ in shell script
  5. Use `trap exit ERR’ in shell script

Specify `bash -e’ as the shebang interpreter

You can add `-e’ to the shebang line, the first line of your shell script:

#!/bin/bash -e

This will execute the shell script with `-e’ active. Note `-e’ can be overridden by invoking bash explicitly (without `-e’):

$ bash shell_script
Example

Create this shell script example.sh and make it executable with chmod u+x example.sh:

#!/bin/bash -e
echo begin
false     # This should exit bash because `false' returns error
echo end  # This should never be reached

Example run:

$ ./example.sh
begin
$ bash example.sh
begin
end
$

Set ERR trap to exit

By setting an ERR trap you can catch errors as well:

trap command ERR

By setting the command to `exit’, bash exits if an error occurs.

trap exit ERR
Example

Example script example.sh

#!/bin/bash
trap exit ERR
echo begin
false
echo end

Example run:

$ ./example.sh
begin
$

The non-zero exit status of `false’ is catched by the error trap. The error trap exits and `echo end’ is never reached.

Solutions revisited: Combining the tools

Executed in subshell, trap on exit

#!/bin/bash
# --- subshell_trap.sh -------------------------------------------------
# Example script for handling bash errors.  Exit on error.  Trap exit.
# This script is supposed to run in a subshell.
# See also: http://fvue.nl/wiki/Bash:_Error_handling
 
    # Let shell functions inherit ERR trap.  Same as `set -E'.
set -o errtrace 
    # Trigger error when expanding unset variables.  Same as `set -u'.
set -o nounset
    #  Trap non-normal exit signals: 1/HUP, 2/INT, 3/QUIT, 15/TERM, ERR
    #  NOTE1: - 9/KILL cannot be trapped.
    #+        - 0/EXIT isn't trapped because:
    #+          - with ERR trap defined, trap would be called twice on error
    #+          - with ERR trap defined, syntax errors exit with status 0, not 2
    #  NOTE2: Setting ERR trap does implicit `set -o errexit' or `set -e'.
trap onexit 1 2 3 15 ERR
 
 
#--- onexit() -----------------------------------------------------
#  @param $1 integer  (optional) Exit status.  If not set, use `$?'
 
function onexit() {
    local exit_status=${1:-$?}
    echo Exiting $0 with $exit_status
    exit $exit_status
}
 
 
 
# myscript
 
 
 
    # Allways call `onexit' at end of script
onexit

Rationale

+-------+   +----------+  +--------+  +------+
| shell |   | subshell |  | script |  | trap |
+-------+   +----------+  +--------+  +------+
     :           :            :           :
    +-+         +-+          +-+  error  +-+
    | |         | |          | |-------->| |
    | |  exit   | |          | !         | |
    | |<-----------------------------------+
    +-+          :            :           :
     :           :            :           :

Figure 1. Trap in executed script
When a script is executed from a shell, bash will create a subshell in which the script is run. If a trap catches an error, and the trap says `exit’, this will cause the subshell to exit.

Sourced in current shell

If the script is sourced (included) in the current shell, you have to worry about restoring shell options and shell traps. If they aren’t restored, they might cause problems in other programs which rely on specific settings.

#!/bin/bash
#--- listing6.inc.sh ---------------------------------------------------
# Demonstration of ERR trap being reset by foo_deinit() with the use
# of `errtrace'.
# Example run:
#
#    $ set +o errtrace         # Make sure errtrace is not set (bash default)
#    $ trap - ERR              # Make sure no ERR trap is set (bash default)
#    $ . listing6.inc.sh       # Source listing6.inc.sh
#    $ foo                     # Run foo()
#    foo_init
#    Entered `trap-loop'
#    trapped
#    This is always executed - with or without a trap occurring
#    foo_deinit
#    $ trap                    # Check if ERR trap is reset.
#    $ set -o | grep errtrace  # Check if the `errtrace' setting is...
#    errtrace        off        # ...restored.
#    $
#
# See: http://fvue.nl/wiki/Bash:_Error_handling
 
function foo_init {
    echo foo_init 
    fooOldErrtrace=$(set +o | grep errtrace)
    set -o errtrace
    trap 'echo trapped; break' ERR   # Set ERR trap 
}
function foo_deinit {
    echo foo_deinit
    trap - ERR                # Reset ERR trap
    eval $fooOldErrtrace      # Restore `errtrace' setting
    unset fooOldErrtrace      # Delete global variable
}
function foo {
    foo_init
        # `trap-loop'
    while true; do
        echo Entered `trap-loop'
        false
        echo This should never be reached because the `false' above is trapped
        break
    done
    echo This is always executed - with or without a trap occurring
    foo_deinit
}

Todo

  • an existing ERR trap must be restored and called
  • test if the `trap-loop’ is reached if the script breaks from a nested loop

Rationale

`Exit’ trap in sourced script

When the script is sourced in the current shell, it’s not possible to use `exit’ to terminate the program: This would terminate the current shell as well, as shown in the picture underneath.

+-------+                 +--------+  +------+
| shell |                 | script |  | trap |
+-------+                 +--------+  +------+
    :                         :           :
   +-+                       +-+  error  +-+
   | |                       | |-------->| |
   | |                       | |         | |
   | | exit                  | |         | |
<------------------------------------------+
    :                         :           :

Figure 2. `Exit’ trap in sourced script
When a script is sourced from a shell, bash will run the script in the current shell. If a trap catches an error, and the trap says `exit’, this will cause the current shell to exit.

`Break’ trap in sourced script

A solution is to introduce a main loop in the program, which is terminated by a `break’ statement within the trap.

+-------+    +--------+  +--------+   +------+
| shell |    | script |  | `loop' |   | trap |
+-------+    +--------+  +--------+   +------+
     :           :            :          :  
    +-+         +-+          +-+  error +-+
    | |         | |          | |------->| |
    | |         | |          | |        | |
    | |         | |  break   | |        | |
    | |  return | |<----------------------+
    | |<----------+           :          :
    +-+          :            :          :
     :           :            :          :

Figure 3. `Break’ trap in sourced script
When a script is sourced from a shell, e.g. . ./script, bash will run the script in the current shell. If a trap catches an error, and the trap says `break’, this will cause the `loop’ to break and to return to the script.

For example:

#!/bin/bash
#--- listing3.sh -------------------------------------------------------
# See: http://fvue.nl/wiki/Bash:_Error_handling

trap 'echo trapped; break' ERR;  # Set ERR trap

function foo { echo foo; false; }  # foo() exits with error

    # `trap-loop'
while true; do
    echo Entered `trap-loop'
    foo
    echo This is never reached
    break
done

echo This is always executed - with or without a trap occurring

trap - ERR  # Reset ERR trap

Listing 3. `Break’ trap in sourced script
When a script is sourced from a shell, e.g. ./script, bash will run the script in the current shell. If a trap catches an error, and the trap says `break’, this will cause the `loop’ to break and to return to the script.

Example output:

$> source listing3.sh
Entered `trap-loop'
foo
trapped
This is always executed after a trap
$>
Trap in function in sourced script without `errtrace’

A problem arises when the trap is reset from within a function of a sourced script. From the bash manual, set -o errtrace or set -E:

If set, any trap on `ERR’ is inherited by shell functions, command

substitutions, and commands executed in a subshell environment.

The `ERR’ trap is normally not inherited in such cases.

So with errtrace not set, a function does not know of any `ERR’ trap set, and thus the function is unable to reset the `ERR’ trap. For example, see listing 4 underneath.

#!/bin/bash
#--- listing4.inc.sh ---------------------------------------------------
# Demonstration of ERR trap not being reset by foo_deinit()
# Example run:
# 
#    $> set +o errtrace     # Make sure errtrace is not set (bash default)
#    $> trap - ERR          # Make sure no ERR trap is set (bash default)
#    $> . listing4.inc.sh   # Source listing4.inc.sh
#    $> foo                 # Run foo()
#    foo_init
#    foo
#    foo_deinit             # This should've reset the ERR trap...
#    $> trap                # but the ERR trap is still there:
#    trap -- 'echo trapped' ERR
#    $> trap

# See: http://fvue.nl/wiki/Bash:_Error_handling

function foo_init   { echo foo_init 
                      trap 'echo trapped' ERR;} # Set ERR trap 

function foo_deinit { echo foo_deinit
                      trap - ERR             ;} # Reset ERR trap

function foo        { foo_init
                      echo foo
                      foo_deinit             ;}

Listing 4. Trap in function in sourced script
foo_deinit() is unable to unset the ERR trap, because errtrace is not set.

Trap in function in sourced script with ‘errtrace’

The solution is to set -o errtrace. See listing 5 underneath:

#!/bin/bash
#--- listing5.inc.sh ---------------------------------------------------
# Demonstration of ERR trap being reset by foo_deinit() with the use
# of `errtrace'.
# Example run:
#
#    $> set +o errtrace         # Make sure errtrace is not set (bash default)
#    $> trap - ERR              # Make sure no ERR trap is set (bash default)
#    $> . listing5.inc.sh       # Source listing5.inc.sh
#    $> foo                     # Run foo()
#    foo_init
#    foo
#    foo_deinit                 # This should reset the ERR trap...
#    $> trap                    # and it is indeed.
#    $> set +o | grep errtrace  # And the `errtrace' setting is restored.
#    $>
#
# See: http://fvue.nl/wiki/Bash:_Error_handling

function foo_init   { echo foo_init 
                      fooOldErrtrace=$(set +o | grep errtrace)
                      set -o errtrace
                      trap 'echo trapped' ERR   # Set ERR trap 
                    }
function foo_deinit { echo foo_deinit
                      trap - ERR                # Reset ERR trap
                      eval($fooOldErrtrace)     # Restore `errtrace' setting
                      fooOldErrtrace=           # Delete global variable
                    }
function foo        { foo_init
                      echo foo
                      foo_deinit             ;}
`Break’ trap in function in sourced script with `errtrace’

Everything combined in listing 6 underneath:

#!/bin/bash
#--- listing6.inc.sh ---------------------------------------------------
# Demonstration of ERR trap being reset by foo_deinit() with the use
# of `errtrace'.
# Example run:
#
#    $> set +o errtrace         # Make sure errtrace is not set (bash default)
#    $> trap - ERR              # Make sure no ERR trap is set (bash default)
#    $> . listing6.inc.sh       # Source listing6.inc.sh
#    $> foo                     # Run foo()
#    foo_init
#    Entered `trap-loop'
#    trapped
#    This is always executed - with or without a trap occurring
#    foo_deinit
#    $> trap                    # Check if ERR trap is reset.
#    $> set -o | grep errtrace  # Check if the `errtrace' setting is...
#    errtrace        off        # ...restored.
#    $>
#
# See: http://fvue.nl/wiki/Bash:_Error_handling

function foo_init {
    echo foo_init 
    fooOldErrtrace=$(set +o | grep errtrace)
    set -o errtrace
    trap 'echo trapped; break' ERR   # Set ERR trap 
}
function foo_deinit {
    echo foo_deinit
    trap - ERR                # Reset ERR trap
    eval $fooOldErrtrace      # Restore `errtrace' setting
    unset fooOldErrtrace      # Delete global variable
}
function foo {
    foo_init
        # `trap-loop'
    while true; do
        echo Entered `trap-loop'
        false
        echo This should never be reached because the `false' above is trapped
        break
    done
    echo This is always executed - with or without a trap occurring
    foo_deinit
}

Test

#!/bin/bash

    # Tests

    # An erroneous command should have exit status 127.
    # The erroneous command should be trapped by the ERR trap.
#erroneous_command

    #  A simple command exiting with a non-zero status should have exit status
    #+ <> 0, in this case 1.  The simple command is trapped by the ERR trap.
#false

    # Manually calling 'onexit'
#onexit

    # Manually calling 'onexit' with exit status
#onexit 5

    #  Killing a process via CTRL-C (signal 2/SIGINT) is handled via the SIGINT trap
    #  NOTE: `sleep' cannot be killed via `kill' plus 1/SIGHUP, 2/SIGINT, 3/SIGQUIT
    #+       or 15/SIGTERM.
#echo $$; sleep 20

    #  Killing a process via 1/SIGHUP, 2/SIGQUIT, 3/SIGQUIT or 15/SIGTERM is
    #+ handled via the respective trap.
    #  NOTE: Unfortunately, I haven't found a way to retrieve the signal number from
    #+       within the trap function.
echo $$; while true; do :; done

    # A syntax error is not trapped, but should have exit status 2
#fi

    # An unbound variable is not trapped, but should have exit status 1
    # thanks to 'set -u'
#echo $foo

     # Executing `false' within a function should exit with 1 because of `set -E'
#function foo() {
#    false
#    true
#} # foo()
#foo

echo End of script
   # Allways call 'onexit' at end of script
onexit

See also

Bash: Err trap not reset
Solution for trap - ERR not resetting ERR trap.

Journal

20210114

Another caveat: exit (or an error-trap) executed within «process substitution» doesn’t end outer process. The script underneath keeps outputting «loop1»:

#!/bin/bash
# This script outputs "loop1" forever, while I hoped it would exit all while-loops
set -o pipefail
set -Eeu
 
while true; do
    echo loop1
    while read FOO; do
        echo loop2
        echo FOO: $FOO
    done < <( exit 1 )
done

The ‘< <()’ notation is called process substitution.

See also:

  • https://mywiki.wooledge.org/ProcessSubstitution
  • https://unix.stackexchange.com/questions/128560/how-do-i-capture-the-exit-code-handle-errors-correctly-when-using-process-subs
  • https://superuser.com/questions/696855/why-doesnt-a-bash-while-loop-exit-when-piping-to-terminated-subcommand

Workaround: Use «Here Strings» ([n]<<<word):

#!/bin/bash
# This script will exit correctly if building up $rows results in an error
 
set -Eeu
 
rows=$(exit 1)
while true; do
    echo loop1
    while read FOO; do
        echo loop2
        echo FOO: $FOO
    done <<< "$rows"
done

20060524

#!/bin/bash
#--- traptest.sh --------------------------------------------
# Example script for trapping bash errors.
# NOTE: Why doesn't this scripts catch syntax errors?

    # Exit on all errors
set -e
    # Trap exit
trap trap_exit_handler EXIT


    # Handle exit trap
function trap_exit_handler() {
        # Backup exit status if you're interested...
    local exit_status=$?
        # Change value of $?
    true
    echo $?
    #echo trap_handler $exit_status
} # trap_exit_handler()


    # An erroneous command will trigger a bash error and, because
    # of 'set -e', will 'exit 127' thus falling into the exit trap.
#erroneous_command
    # The same goes for a command with a false return status
#false

    # A manual exit will also fall into the exit trap
#exit 5

    # A syntax error isn't catched?
fi

    # Disable exit trap
trap - EXIT
exit 0

Normally, a syntax error exits with status 2, but when both ‘set -e’ and ‘trap EXIT’ are defined, my script exits with status 0. How can I have both ‘errexit’ and ‘trap EXIT’ enabled, *and* catch syntax errors
via exit status? Here’s an example script (test.sh):

set -e
trap 'echo trapped: $?' EXIT
fi

$> bash test.sh; echo $?: $?
test.sh: line 3: syntax error near unexpected token `fi'
trapped: 0
$?: 0

More trivia:

  • With the line ‘#set -e’ commented, bash traps 258 and returns an exit status of 2:
trapped: 258
$?: 2
  • With the line ‘#trap ‘echo trapped $?’ EXIT’ commented, bash returns an exit status of 2:
$?: 2
  • With a bogus function definition on top, bash returns an exit status of 2, but no exit trap is executed:
function foo() { foo=bar }
set -e
trap 'echo trapped: $?' EXIT
fi
fred@linux:~>bash test.sh; echo $?: $?
test.sh: line 4: syntax error near unexpected token `fi'
test.sh: line 4: `fi'
$?: 2

20060525

Example of a ‘cleanup’ script

trap

Writing Robust Bash Shell Scripts

#!/bin/bash
#--- cleanup.sh ---------------------------------------------------------------
# Example script for trapping bash errors.
# NOTE: Use 'cleanexit [status]' instead of 'exit [status]'

    # Trap not-normal exit signals: 1/HUP, 2/INT, 3/QUIT, 15/TERM
    # @see catch_sig()
trap catch_sig 1 2 3 15
    # Trap errors (simple commands exiting with a non-zero status)
    # @see catch_err()
trap catch_err ERR


#--- cleanexit() --------------------------------------------------------------
#  Wrapper around 'exit' to cleanup on exit.
#  @param $1 integer  Exit status.  If $1 not defined, exit status of global
#+                    variable 'EXIT_STATUS' is used.  If neither $1 or
#+                    'EXIT_STATUS' defined, exit with status 0 (success).
function cleanexit() {
    echo "Exiting with ${1:-${EXIT_STATUS:-0}}"
    exit ${1:-${EXIT_STATUS:-0}}
} # cleanexit()


#--- catch_err() --------------------------------------------------------------
#  Catch ERR trap.
#  This traps simple commands exiting with a non-zero status.
#  See also: info bash | "Shell Builtin Commands" | "The Set Builtin" | "-e"
function catch_err() {
    local exit_status=$?
    echo "Inside catch_err"
    cleanexit $exit_status
} # catch_err()


#--- catch_sig() --------------------------------------------------------------
# Catch signal trap.
# Trap not-normal exit signals: 1/HUP, 2/INT, 3/QUIT, 15/TERM
# @NOTE1: Non-trapped signals are 0/EXIT, 9/KILL.
function catch_sig() {
    local exit_status=$?
    echo "Inside catch_sig"
    cleanexit $exit_status
} # catch_sig()


    # An erroneous command should have exit status 127.
    # The erroneous command should be trapped by the ERR trap.
#erroneous_command

    # A command returning false should have exit status <> 0
    # The false returning command should be trapped by the ERR trap.
#false

    # Manually calling 'cleanexit'
#cleanexit

    # Manually calling 'cleanexit' with exit status
#cleanexit 5

    # Killing a process via CTRL-C is handled via the SIGINT trap
#sleep 20

    # A syntax error is not trapped, but should have exit status 2
#fi

    # Allways call 'cleanexit' at end of script
cleanexit

blog comments powered by

Advertisement

blog comments powered by

Понравилась статья? Поделить с друзьями:
  • Bash error dev null
  • Bash error codes
  • Bash echo write error invalid argument
  • Bash disable error output
  • Bash deb команда не найдена как исправить