Bash ignore error script

I am using following options set -o pipefail set -e In bash script to stop execution on error. I have ~100 lines of script executing and I don't want to check return code of every line in the scr...

I am using following options

set -o pipefail
set -e

In bash script to stop execution on error. I have ~100 lines of script executing and I don’t want to check return code of every line in the script.

But for one particular command, I want to ignore the error. How can I do that?

Ryan Shillington's user avatar

asked Jun 27, 2012 at 17:34

Vivek Goel's user avatar

Vivek GoelVivek Goel

22.3k29 gold badges109 silver badges184 bronze badges

The solution:

particular_script || true

Example:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three will be never printed.

Also, I want to add that when pipefail is on,
it is enough for shell to think that the entire pipe has non-zero exit code
when one of commands in the pipe has non-zero exit code (with pipefail off it must the last one).

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0

R. Oosterholt's user avatar

answered Jun 27, 2012 at 17:37

Igor Chubin's user avatar

Igor ChubinIgor Chubin

60.1k11 gold badges118 silver badges140 bronze badges

7

Just add || true after the command where you want to ignore the error.

answered Jun 27, 2012 at 17:37

Lars Kotthoff's user avatar

Lars KotthoffLars Kotthoff

106k15 gold badges203 silver badges201 bronze badges

3

Don’t stop and also save exit status

Just in case if you want your script not to stop if a particular command fails and you also want to save error code of failed command:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE

Ciro Santilli OurBigBook.com's user avatar

answered Aug 17, 2017 at 8:14

Arslan Qadeer's user avatar

5

More concisely:

! particular_script

From the POSIX specification regarding set -e (emphasis mine):

When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.

answered Apr 21, 2015 at 23:13

Lily Finley's user avatar

Lily FinleyLily Finley

2,7171 gold badge15 silver badges11 bronze badges

5

Instead of «returning true», you can also use the «noop» or null utility (as referred in the POSIX specs) : and just «do nothing». You’ll save a few letters. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."

answered Nov 17, 2016 at 8:51

Timo's user avatar

TimoTimo

3,27330 silver badges24 bronze badges

5

If you want to prevent your script failing and collect the return code:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode is collected no matter whether command succeeds or fails.

answered Sep 10, 2019 at 10:33

volingas's user avatar

volingasvolingas

9639 silver badges20 bronze badges

2

Thanks for the simple solution here from above:

<particular_script/command> || true

The following construction could be used for additional actions/troubleshooting of script steps and additional flow control options:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

We can brake the further actions and exit 1 if required.

answered May 20, 2020 at 13:28

Almaz Gareev's user avatar

I found another way to solve this:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

You can turn off failing on errors by set +e this will now ignore all errors after that line. Once you are done, and you want the script to fail again on any error, you can use set -e.

After applying set +e the find does not fail the whole script anymore, when files are not found. At the same time, error messages
from find are still printed, but the whole script continues to execute. So it is easy to debug if that causes the problem.

This is useful for CI & CD (for example in GitHub Actions).

answered Feb 23, 2020 at 13:35

Konard's user avatar

KonardKonard

2,06826 silver badges21 bronze badges

3

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

Example of using this to create a log file

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi

answered Mar 25, 2020 at 3:25

Robert.C's user avatar

Robert.CRobert.C

511 silver badge1 bronze badge

I have been using the snippet below when working with CLI tools and I want to know if some resource exist or not, but I don’t care about the output.

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi

answered Jun 23, 2018 at 22:12

Payman's user avatar

PaymanPayman

2,3101 gold badge12 silver badges18 bronze badges

1

while || true is preferred one, but you can also do

var=$(echo $(exit 1)) # it shouldn't fail

answered Apr 25, 2019 at 15:17

Foto Blysk's user avatar

Foto BlyskFoto Blysk

3342 silver badges11 bronze badges

I kind of like this solution :

: `particular_script`

The command/script between the back ticks is executed and its output is fed to the command «:» (which is the equivalent of «true»)

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

edit: Fixed ugly typo

answered May 21, 2019 at 8:56

Q-life's user avatar

1

Bash, or the Bourne-Again Shell, is a powerful command-line interface (CLI) that is commonly used in Linux and Unix systems. When working with Bash, it is important to understand how to handle errors that may occur during the execution of commands. In this article, we will discuss various ways to understand and ignore errors in Bash. Bash scripting is a powerful tool for automating and simplifying various tasks in Linux and Unix systems. However, errors can occur during the execution of commands and can cause scripts to fail. In this article, we will explore the various ways to understand and handle errors in Bash. We will look at ways to check the exit status code and error messages of commands, as well as techniques for ignoring errors when necessary. By understanding and properly handling errors, you can ensure that your Bash scripts run smoothly and achieve the desired outcome.

Step-by-step approach for understanding and ignoring errors in Bash:

Step 1: Understand how errors are generated in Bash.

  • When a command is executed, it returns an exit status code.
  • A successful command will have an exit status of 0, while a failed command will have a non-zero exit status.
  • Error messages are generated when a command returns a non-zero exit status code.

Step 2: Check the exit status code of a command.

  • To check the exit status code of a command, you can use the $? variable, which holds the exit status of the last executed command.
  • For example, after executing the command ls non_existent_directory, you can check the exit status code by running echo $? The output
  • will be non-zero (e.g., 2) indicating that the command failed.

Step 3: Check the error message of a command.

  • To check the error message of a command, you can redirect the standard error output (stderr) to a file or to the standard output (stdout) using the 2> operator.
  • For example, you can redirect the stderr of the command ls non_existent_directory to a file by running ls non_existent_directory 2> error.log. Then you can view the error message by running cat error.log.

Step 4: Use the set -e command.

  • The set -e command causes the script to exit immediately if any command exits with a non-zero status. This can be useful for detecting and handling errors early on in a script.
  • For example, if you run set -e followed by ls non_existent_directory, the script will exit immediately with an error message. 

Step 5: Ignore errors when necessary.

  • To ignore errors, you can use the command || true construct. This construct allows you to execute a command, and if it returns a non-zero exit status, the command following the || operator (in this case, true) will be executed instead.
  • For example, you can run rm non_existent_file || true to remove a file that does not exist without exiting with an error.
  • Another way to ignore errors is to use the command 2> /dev/null construct, which redirects the standard error output (stderr) of a command to the null device, effectively ignoring any error messages.
  • Additionally, you can use the command 2>&1 >/dev/null construct to ignore both standard error and standard output.
  • You can also use the command || : construct which allows you to execute a command and if it returns a non-zero exit status, the command following the || operator (in this case, 🙂 will be executed instead. The: command is a no-op command that does nothing, effectively ignoring the error.

Practical Explanation for Understanding Errors

First, let’s examine how errors are generated in Bash. When a command is executed, it returns an exit status code. This code indicates whether the command was successful (exit status 0) or not (non-zero exit status). For example, the following command attempts to list the files in a directory that does not exist:

$ ls non_existent_directory
ls: cannot access 'non_existent_directory': No such file or directory

As you can see, the command generated an error message and returned a non-zero exit status code. To check the exit status code of a command, you can use the $? variable, which holds the exit status of the last executed command.

$ echo $?
2

In addition to the exit status code, you can also check the standard error output (stderr) of a command to understand errors. This can be done by redirecting the stderr to a file or to the standard output (stdout) using the 2> operator.

For example, the following script will redirect the stderr of a command to a file:

$ ls non_existent_directory 2> error.log
$ cat error.log
ls: cannot access 'non_existent_directory': No such file or directory

You can also redirect the stderr to the stdout using the 2>&1 operator, which allows you to see the error message along with the standard output of the command.

$ ls non_existent_directory 2>&1
ls: cannot access 'non_existent_directory': No such file or directory

Another useful tool for understanding errors is the set -e command, which causes the script to exit immediately if any command exits with a non-zero status. This can be useful for detecting and handling errors early on in a script.

$ set -e
$ ls non_existent_directory
# as soon as you hit enter this will exit shell and will close the terminal.

After this command script will exit from the shell if the exit code is nonzero.

Practical Explanation for Ignoring Errors

While it is important to handle errors in Bash scripts, there may be certain situations where you want to ignore errors and continue running the script. In this section, we will discuss different methods for ignoring errors in Bash and provide examples of how to implement them.

Heredoc

Heredoc is a feature in Bash that allows you to specify a string or command without having to escape special characters. This can be useful when you want to ignore errors that may occur while executing a command. The following example demonstrates how to use Heredoc to ignore errors.

#!/bin/bash

# Example of ignoring errors using Heredoc

# The `command` will fail but it will not stop execution
cat <<EOF | while read line; do
  echo $line
done
command that will fail
EOF

# Rest of the script

In this example, the command that is inside the Heredoc will fail, but the script will not stop execution. This is because the output of the command is piped to the while loop, which reads the output and ignores the error.

Pipefail

The pipe fails option in Bash can be used to change the behavior of pipelines so that the exit status of the pipeline is the value of the last (rightmost) command to exit with a non-zero status or zero if all commands exit successfully. This can be useful when you want to ignore errors that may occur while executing multiple commands in a pipeline. The following example demonstrates how to use the pipe fail option to ignore errors.

#!/bin/bash

# Example of ignoring errors using pipefail

# The `command1` will fail but it will not stop execution
set -o pipefail
command1 | command2

# Rest of the script

In this example, command1 will fail, but command2 will continue to execute, and the script will not stop execution.

Undefined Variables

By default, Bash will stop the execution of a script if an undefined variable is used. However, you can use the -u option to ignore this behavior and continue running the script even if an undefined variable is used. The following example demonstrates how to ignore undefined variables.

#!/bin/bash

# Example of ignoring undefined variables

set +u

echo $undefined_variable

# Rest of the script

In this example, the script will not stop execution when an undefined variable is used.

Compiling and Interpreting

When compiling or interpreting a script, errors may occur. However, these errors can be ignored by using the -f option when running the script. The following example demonstrates how to ignore errors when compiling or interpreting a script.

#!/bin/bash

# Example of ignoring errors when compiling or interpreting

bash -f script.sh

# Rest of the script

In this example, the script will continue to run even if there are errors during the compilation or interpretation process.

Traps

A trap is a way to execute a command or a set of commands when a specific signal is received by the script. This can be useful when you want to ignore errors and run a cleanup command instead. The following example demonstrates how to use a trap to ignore errors.

#!/bin/bash

# Example of ignoring errors using a trap

# Set a trap to run the cleanup function when an error occurs
trap cleanup ERR

# Function to run when an error occurs
cleanup() {
  echo "Cleaning up before exiting..."
}

# Command that will cause an error
command_that_will_fail

# Rest of the script

In this example, when the command_that_will_fail causes an error, the script will execute the cleanup function instead of stopping execution. This allows you to perform any necessary cleanup before exiting the script.

Examples of Bash for Error Handling:

Example 1: Error Handling Using a Conditional Condition

One way to handle errors in Bash is to use a conditional statement. The following example demonstrates how to check for a specific error and handle it accordingly.

#!/bin/bash

# Example of error handling using a conditional condition

file=example.txt

if [ ! -f $file ]; then
  echo "Error: $file does not exist"
  exit 1
fi

# Rest of the script

In this example, we check if the file “example.txt” exists using the -f option of the [ command. If the file does not exist, the script will print an error message and exit with a status code of 1. This allows the script to continue running if the file exists and exit if it does not.

Example 2: Error Handling Using the Exit Status Code

Another way to handle errors in Bash is to check the exit status code of a command. Every command in Bash returns an exit status code when it completes, with a code of 0 indicating success and any other code indicating an error. The following example demonstrates how to check the exit status code of a command and handle it accordingly.

#!/bin/bash

# Example of error handling using the exit status code

command1

if [ $? -ne 0 ]; then
  echo "Error: command1 failed"
  exit 1
fi

# Rest of the script

In this example, the script runs the command “command1” and then checks the exit status code using the special variable $?. If the exit status code is not 0, the script will print an error message and exit with a status code of 1.

Example 3: Stop the Execution on the First Error

When running a script, it can be useful to stop the execution on the first error that occurs. This can be achieved by using the set -e command, which tells Bash to exit the script if any command exits with a non-zero status code.

#!/bin/bash

# Stop execution on the first error

set -e

command1
command2
command3

# Rest of the script

In this example, if any of the commands “command1”, “command2” or “command3” fail, the script will exit immediately.

Example 4: Stop the Execution for Uninitialized Variable

Another way to stop execution on error is if an uninitialized variable is used during script execution. This can be achieved by using the set -u command, which tells Bash to exit the script if any uninitialized variable is used.

#!/bin/bash

# Stop execution for uninitialized variable

set -u

echo $uninitialized_variable

# Rest of the script

In this example, if the uninitialized_variable is not defined, the script will exit immediately.

Conclusion

In conclusion, understanding and ignoring errors in Bash is an important aspect of working with the command-line interface. By checking the exit status code of a command, its associated error message, and redirecting the stderr to a file or the stdout, you can understand what went wrong. And by using the command || true, command 2> /dev/null, command 2>&1 >/dev/null, and command || : constructs, you can ignore errors when necessary. It’s always a good practice to test these constructs in a testing environment before using them in production.

Ignoring errors in a section of a Bash script

For simonw/museums#32 I wanted to have certain lines in my Bash script ignore any errors: lines that used sqlite-utils to add columns and configure FTS, but that might fail with an error if the column already existed or FTS had already been configured.

This tip on StackOverflow lead me to the following recipe:

#!/bin/bash
set -euo pipefail

yaml-to-sqlite browse.db museums museums.yaml --pk=id
python annotate_nominatum.py browse.db
python annotate_timestamps.py
# Ignore errors in following block until set -e:
set +e
sqlite-utils add-column browse.db museums country 2>/dev/null
sqlite3 browse.db < set-country.sql
sqlite-utils disable-fts browse.db museums 2>/dev/null
sqlite-utils enable-fts browse.db museums 
  name description country osm_city 
  --tokenize porter --create-triggers 2>/dev/null
set -e

Everything between the set +e and the set -e lines can now error without the Bash script itself failing.

The failing lines were still showing a bunch of Python tracebacks. I fixed that by redirecting their standard error output to /dev/null like this:

sqlite-utils disable-fts browse.db museums 2>/dev/null

Robust error handling in Bash

Errors happen. Even if we write a perfect program, it might be provided with invalid input, or unexpectedly disconnected from the network. The key to writing reliable software isn’t to prevent all errors, but to ensure that all errors are handled in a predictable way.

Bash does not enforce precise error handling. In fact, by default it simply ignores most errors. However, with a bit of care it is possible to write robust, reliable scripts that keep you and your users happy. Here are some error handling practices to keep in mind.

What do we mean by errors?

Bash doesn’t have exceptions or error types as we might be used to in other languages. However, every command, whether it’s built-in to Bash or an external program, returns an «exit status code» between 0 and 255 when it finishes executing. Successful commands return 0 , while commands that fail return a code between 1 and 255 .

When I talk about «errors» in Bash in this post, I’m referring to any command which exits with a non-zero exit code in a context where it isn’t explicitly expected. For example, if you had a program that started with

Exit fullscreen mode

and example.txt did not exist, that would be an error. Nothing is handling the failure, so the program would either crash or continue in an invalid state. However if you have an if statement like

Exit fullscreen mode

the command test -e example.txt may fail, but the if statement is expecting its condition to be a command that might fail, and it handles that case automatically. I do not consider that an «error» for the purpose of this post. The same reasoning applies to cases like while COMMAND; do . and COMMAND || return 0 ; see the Bash manual for the full list of exceptions.

Simple errors

By default, Bash scripts will ignore most errors and continue running. The first thing we need to do in our scripts is enable Bash’s basic error handling options, as follows.

Exit fullscreen mode

Here we enabling three options at once. Let’s break them down.

set -e (aka -o errexit ) causes most failing commands to immediately return from the enclosing function, propagating their error exit status code to the calling function. If the calling function also doesn’t handle the error, it will continue up the stack, eventually exiting the script with that exit status code. (Note that there are still some cases where errors can be silently ignored, discussed below.)

set -u (aka -o nounset ) makes it an error to refer to a variable like $X if it hasn’t been defined, either in the script or as an environment variable, instead of treating it as an empty string. Often, this is a typo and a bug. There are certainly some cases where you’ll need to handle possibly-undefined variables, but they should be indicated explicitly: you can use $ instead of $X to indicate where you’d like to use an empty string if a variable isn’t defined.

set -o pipefail prevents errors from being silently ignored in pipelines (when the output of one command is being piped to the input of another). For example, consider:

Exit fullscreen mode

By default, the exit status of the entire pipeline will just be that of the last command, sort . This can succeed even if example.txt does not exist and an earlier command like cat fails. pipefail changes this behaviour so that the pipeline is marked as failed if any of the commands fail. (Subsequent commands in the pipeline will still be executed. If multiple fail, the exit status of the last failing command will be used.)

Setting set -euo pipefail is a very common practice for many shells, but for Bash in particular there’s one more option you should also be setting:

Exit fullscreen mode

By default, when you use command substitution in Bash, the -e setting is not applied. For example, if we had

Exit fullscreen mode

the command would successfully output the result of date , even though the failing false command should have exited the script first. Enabling inherit_errexit allows the command substitution to inherit our -e setting, so date will not be run. (However, please note that the error status of the command substitution is still ignored. Even though the parenthesized expression returned a nonzero exit status, echo will still run successfully. This is discussed in more detail below.)

ShellCheck

Adopting those settings made my scripts much more reliable, but I was still finding some bugs in them. Many came from me misunderstanding subtleties of Bash’s syntax, where my code wasn’t doing what I thought it was doing. I might forget which terms need quoting in a condition like [[ $x -eq «$y» ]] , or where I can and can’t omit the $ before a variable in an expression like $(( x = y )) . I tried to keep the rules straight, but there were too many to absorb at once and it felt hopeless, until I discovered ShellCheck.

ShellCheck is a static analysis tool/linter for Bash scripts, and it is invaluable. I use it in VS Code (extension) and run it in CI. It flags cases where your code might not be doing what you expect, with links to wiki pages explaining the problem and potential alternatives.

Most of my recent Bash learnings have started with a ShellCheck warning code making me aware of an edge case or capability that I hadn’t considered. Like any linter, you may occasionally need to ignore its warnings with an annotation like # shellcheck disable=SC2034 , but I’ve found its advice is usually very good, even when it seemed counterintuitive at first.

Even with ShellCheck, there are still some subtle cases where you can silence errors without realizing it, but not many.

Subshells

A lot of things about Bash have surprised me, but this was the most shocking: when you use parentheses to group commands, Bash forks the entire process to create a «subshell» child process running the parenthesized code!

Exit fullscreen mode

This is why if you try to set a global variable from inside of parentheses, the change won’t be visible outside: you’re only setting the value in the child process.

Exit fullscreen mode

This usually doesn’t cause a problem for error handling—our settings are propagated to the subshell, and the exit status of the subshell is propagated back. However, there is one major exception.

The unfortunate case of command substitution

Even with every available setting enabled, failures in command substitution subshells are usually silenced/masked and do not cause a failure in the original shell process. For example:

Exit fullscreen mode

Exit fullscreen mode

As far as I can tell, there is no way to change this behaviour, and ShellCheck can’t warn about it. If there is some way I’ve missed, please let me know! There are workarounds, but they’re clunky.

The exit status of these subshells is returned to the parent shell, however, it’s never checked before it is overwritten by the return status of the original command ( echo in the case above). If we put the command substitution in an assignment expression on its own, instead of as an argument to another command, the exit status won’t be overwritten. For example:

Exit fullscreen mode

Exit fullscreen mode

This will behave properly, with the failure in output=»$(. )» exiting the script.

Bonus suggestions

The default handling of glob expressions in Bash is confusing. Consider the command

Exit fullscreen mode

If builds contains one or more directories whose names start with bin- , you’ll get an argument for each, expanding to something like this:

Exit fullscreen mode

However, if there’s no match, the glob expression isn’t replaced, it’s just passed to the command as-is, typically producing an error or unexpected behaviour:

Exit fullscreen mode

There are two more-reasonable alternative behaviours, and I strongly suggest you set one of them: shopt -s nullglob will replace the glob expression with the empty string if it doesn’t match, and shopt -s failglob will raise an error.

Finally, you should set shopt -s compat»$» with the minimum Bash version you want to support, to reduce the chance of breakage in later versions. For Linux I usually target 42 (February 2011) but macOS only ships with 32 (October 2006).

Conclusion

Writing robust Bash scripts is tricky, but not impossible. Start your scripts with set -euo pipefail; shopt -s inherit_errexit nullglob compat»$» and use ShellCheck, and you’ll be 90% of the way there!

Appendix 1: Bash manual description of the -e / -o errexit setting

Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound 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 following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or || , any command in a pipeline but the last, or if the command’s return value is being inverted with ! . If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR , if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.

Источник

How to catch and handle errors in bash

Last updated on March 28, 2021 by Dan Nanni

In an ideal world, things always work as expected, but you know that’s hardly the case. The same goes in the world of bash scripting. Writing a robust, bug-free bash script is always challenging even for a seasoned system administrator. Even if you write a perfect bash script, the script may still go awry due to external factors such as invalid input or network problems. While you cannot prevent all errors in your bash script, at least you should try to handle possible error conditions in a more predictable and controlled fashion.

That is easier said than done, especially since error handling in bash is notoriously difficult. The bash shell does not have any fancy exception swallowing mechanism like try/catch constructs. Some bash errors may be silently ignored but may have consequences down the line. The bash shell does not even have a proper debugger.

In this tutorial, I’ll introduce basic tips to catch and handle errors in bash. Although the presented error handling techniques are not as fancy as those available in other programming languages, hopefully by adopting the practice, you may be able to handle potential bash errors more gracefully.

Bash Error Handling Tip #1: Check the Exit Status

As the first line of defense, it is always recommended to check the exit status of a command, as a non-zero exit status typically indicates some type of error. For example:

Another (more compact) way to trigger error handling based on an exit status is to use an OR list:

With this OR statement, is executed if and only if returns a non-zero exit status. So you can replace with your own error handling routine. For example:

Bash provides a built-in variable called $? , which tells you the exit status of the last executed command. Note that when a bash function is called, $? reads the exit status of the last command called inside the function. Since some non-zero exit codes have special meanings, you can handle them selectively. For example:

Bash Error Handling Tip #2: Exit on Errors in Bash

When you encounter an error in a bash script, by default, it throws an error message to stderr , but continues its execution in the rest of the script. In fact you see the same behavior in a terminal window; even if you type a wrong command by accident, it will not kill your terminal. You will just see the «command not found» error, but you terminal/bash session will still remain.

This default shell behavior may not be desirable for some bash script. For example, if your script contains a critical code block where no error is allowed, you want your script to exit immediately upon encountering any error inside that code block. To activate this «exit-on-error» behavior in bash, you can use the set command as follows.

Once called with -e option, the set command causes the bash shell to exit immediately if any subsequent command exits with a non-zero status (caused by an error condition). The +e option turns the shell back to the default mode. set -e is equivalent to set -o errexit . Likewise, set +e is a shorthand command for set +o errexit .

However, one special error condition not captured by set -e is when an error occurs somewhere inside a pipeline of commands. This is because a pipeline returns a non-zero status only if the last command in the pipeline fails. Any error produced by previous command(s) in the pipeline is not visible outside the pipeline, and so does not kill a bash script. For example:

If you want any failure in pipelines to also exit a bash script, you need to add -o pipefail option. For example:

Therefore, to protect a critical code block against any type of command errors or pipeline errors, use the following pair of set commands.

Bash Error Handling Tip #3: Try and Catch Statements in Bash

Although the set command allows you to terminate a bash script upon any error that you deem critical, this mechanism is often not sufficient in more complex bash scripts where different types of errors could happen.

To be able to detect and handle different types of errors/exceptions more flexibly, you will need try/catch statements, which however are missing in bash. At least we can mimic the behaviors of try/catch as shown in this trycatch.sh script:

Here we define several custom bash functions to mimic the semantic of try and catch statements. The throw() function is supposed to raise a custom (non-zero) exception. We need set +e , so that the non-zero returned by throw() will not terminate a bash script. Inside catch() , we store the value of exception raised by throw() in a bash variable exception_code , so that we can handle the exception in a user-defined fashion.

Perhaps an example bash script will make it clear how trycatch.sh works. See the example below that utilizes trycatch.sh .

In this example script, we define three types of custom exceptions. We can choose to raise any of these exceptions depending on a given error condition. The OR list || throw allows us to invoke throw() function with a chosen value as a parameter, if returns a non-zero exit status. If is completed successfully, throw() function will be ignored. Once an exception is raised, the raised exception can be handled accordingly inside the subsequent catch block. As you can see, this provides a more flexible way of handling different types of error conditions.

Granted, this is not a full-blown try/catch constructs. One limitation of this approach is that the try block is executed in a sub-shell. As you may know, any variables defined in a sub-shell are not visible to its parent shell. Also, you cannot modify the variables that are defined in the parent shell inside the try block, as the parent shell and the sub-shell have separate scopes for variables.

Conclusion

In this bash tutorial, I presented basic error handling tips that may come in handy when you want to write a more robust bash script. As expected these tips are not as sophisticated as the error handling constructs available in other programming language. If the bash script you are writing requires more advanced error handling than this, perhaps bash is not the right language for your task. You probably want to turn to other languages such as Python.

Let me conclude the tutorial by mentioning one essential tool that every shell script writer should be familiar with. ShellCheck is a static analysis tool for shell scripts. It can detect and point out syntax errors, bad coding practice and possible semantic issues in a shell script with much clarity. Definitely check it out if you haven’t tried it.

If you find this tutorial helpful, I recommend you check out the series of bash shell scripting tutorials provided by Xmodulo.

Support Xmodulo

This website is made possible by minimal ads and your gracious donation via PayPal or credit card

Please note that this article is published by Xmodulo.com under a Creative Commons Attribution-ShareAlike 3.0 Unported License. If you would like to use the whole or any part of this article, you need to cite this web page at Xmodulo.com as the original source.

Xmodulo © 2020 ‒ About ‒ Powered by DigitalOcean

Источник

You can put 2>/dev/null behind a command to suppress errors:

ls /home/cas/thisfolderdoesntexist -> error

ls /home/cas/thisfolderdoesntexist 2>/dev/null -> no output because error is suppressed.

You can also put 2>/dev/null behind a script to run the complete script with errors suppressed:

./script.sh 2>/dev/null

What your doing is redirecting (>) errors (2) to /dev/null. Every piece of data (in this case the output of your command(s)/script) that is redirected to /dev/null will be completely ignored. See it as a trash can for data.


Edit:
2>/dev/null suppresses the output of the command, not the complete pipe. In the example that you gave, you’re supressing errors from the awk command. If the error is comming from the ls command, do the following (this will suppress errors from the ls command):

ls /bootpool 2>/dev/null | grep boot | awk 'NR==1{print $1}'

If the error is comming from the grep command:

ls /bootpool | grep boot 2>/dev/null | awk 'NR==1{print $1}'

I think you get it now.

A good thing to remember:

1 = stdout = normal output of a command

2 = stderr = error output of a command

0 = stdin = input to a command (this isn’t usefull for redirecting, more for logging)


I also improved your script (using shellcheck, you can install it or use their online tool link):

#!/bin/sh
boot=$(find /bootpool/*boot* 2>/dev/null | sed "s|/.*/||")
data=$(find /datapool/*boot* 2>/dev/null | sed "s|/.*/||")
echo "boot"
if [ "$boot" = "boot" ] 
then
        echo "boot"
        pass=$(grep rootpw /bootpool/boot/loader.conf | grep -o '".*"' | sed 's|"||g' | awk 'BEGIN { ORS = " " } { print }')

elif [ "$data" = "boot" ]
then
        pass=$(grep rootpw /datapool/boot/loader.conf | grep -o '".*"' | sed 's|"||g' | awk 'BEGIN { ORS = " " } { print }')

else
        echo "Couldn't find boot in bootpool nor datapool"
        exit
fi

if [ "$pass" = edjos ]
then
        echo "You are at default password. kindly change the password"
        oldpass=$(grep root /etc/master.passwd | awk 'NR==1 { print $1 }' | cut -d ':' -f 2 | sed 's/$/%/g')
        passwd
        newpass=$(grep root /etc/master.passwd | awk 'NR==1 { print $1 }' | cut -d ':' -f 2 | sed 's/$/%/g')
        if [ "$newpass" != "$oldpass" ]
        then              
                if [ "$boot" = "boot" ]
                then 
                        sed -i.bak '/mfsbsd.rootpw/s/edjos//' /bootpool/boot/loader.conf
                        sed -i.bak '/mfsbsd.rootpwhash/d' /bootpool/boot/loader.conf
                        echo "mfsbsd.rootpwhash="$newpass""  >> /bootpool/boot/loader.conf
                        echo "Great! password updated successfully"

                elif [ "$data" = "boot" ]
                then
                        sed -i.bak '/mfsbsd.rootpw/s/edjos//' /datapool/boot/loader.conf
                        sed -i.bak '/mfsbsd.rootpwhash/d' /datapool/boot/loader.conf
                        echo "mfsbsd.rootpwhash="$newpass""  >> /datapool/boot/loader.conf
                        echo "Great! password updated successfully"
                fi
        fi
else
        echo "Great! you are having authorised password"
fi
  1. You were using == but /bin/sh doesn’t make use of that. Only =. When you use /bin/bash, == will actually become usefull. But as you don’t, you need to use =.
  2. I changed the way you set the boot and data variables. The way you did it was inefficient.
  3. When both $boot and $data are empty, the script will catch it instead of letting you continue. This is handy because in your second if statement, when $oldpass and $newpass aren’t equal, it depends on either $boot or $data to contain «boot». But what if they don’t? That’s what the else is for in the first if-statement.
  4. Putting "" around variables. echo $var -> echo "$var"

Jeremy

Jeremy

Posted on Jul 2, 2020

• Updated on Jul 19, 2020

Errors happen. Even if we write a perfect program, it might be provided with invalid input, or unexpectedly disconnected from the network. The key to writing reliable software isn’t to prevent all errors, but to ensure that all errors are handled in a predictable way.

Bash does not enforce precise error handling. In fact, by default it simply ignores most errors. However, with a bit of care it is possible to write robust, reliable scripts that keep you and your users happy. Here are some error handling practices to keep in mind.

What do we mean by errors?

Bash doesn’t have exceptions or error types as we might be used to in other languages. However, every command, whether it’s built-in to Bash or an external program, returns an «exit status code» between 0 and 255 when it finishes executing. Successful commands return 0, while commands that fail return a code between 1 and 255.

When I talk about «errors» in Bash in this post, I’m referring to any command which exits with a non-zero exit code in a context where it isn’t explicitly expected. For example, if you had a program that started with

cat example.txt

Enter fullscreen mode

Exit fullscreen mode

and example.txt did not exist, that would be an error. Nothing is handling the failure, so the program would either crash or continue in an invalid state. However if you have an if statement like

if test -e example.txt; then
  echo "Example found"
else
  echo "Example not found"
fi

Enter fullscreen mode

Exit fullscreen mode

the command test -e example.txt may fail, but the if statement is expecting its condition to be a command that might fail, and it handles that case automatically. I do not consider that an «error» for the purpose of this post. The same reasoning applies to cases like while COMMAND; do ... and COMMAND || return 0; see the Bash manual for the full list of exceptions.

Simple errors

By default, Bash scripts will ignore most errors and continue running. The first thing we need to do in our scripts is enable Bash’s basic error handling options, as follows.

set -euo pipefail

Enter fullscreen mode

Exit fullscreen mode

Here we enabling three options at once. Let’s break them down.

set -e (aka -o errexit) causes most failing commands to immediately return from the enclosing function, propagating their error exit status code to the calling function. If the calling function also doesn’t handle the error, it will continue up the stack, eventually exiting the script with that exit status code. (Note that there are still some cases where errors can be silently ignored, discussed below.)

set -u (aka -o nounset) makes it an error to refer to a variable like $X if it hasn’t been defined, either in the script or as an environment variable, instead of treating it as an empty string. Often, this is a typo and a bug. There are certainly some cases where you’ll need to handle possibly-undefined variables, but they should be indicated explicitly: you can use ${X-} instead of $X to indicate where you’d like to use an empty string if a variable isn’t defined.

set -o pipefail prevents errors from being silently ignored in pipelines (when the output of one command is being piped to the input of another). For example, consider:

cat example.txt | grep metadata | sort

Enter fullscreen mode

Exit fullscreen mode

By default, the exit status of the entire pipeline will just be that of the last command, sort. This can succeed even if example.txt does not exist and an earlier command like cat fails. pipefail changes this behaviour so that the pipeline is marked as failed if any of the commands fail. (Subsequent commands in the pipeline will still be executed. If multiple fail, the exit status of the last failing command will be used.)

Setting set -euo pipefail is a very common practice for many shells, but for Bash in particular there’s one more option you should also be setting:

shopt -s inherit_errexit

Enter fullscreen mode

Exit fullscreen mode

By default, when you use command substitution in Bash, the -e setting is not applied. For example, if we had

echo "Command output: $(false; date)"

Enter fullscreen mode

Exit fullscreen mode

the command would successfully output the result of date, even though the failing false command should have exited the script first. Enabling inherit_errexit allows the command substitution to inherit our -e setting, so date will not be run. (However, please note that the error status of the command substitution is still ignored. Even though the parenthesized expression returned a nonzero exit status, echo will still run successfully. This is discussed in more detail below.)

ShellCheck

Adopting those settings made my scripts much more reliable, but I was still finding some bugs in them. Many came from me misunderstanding subtleties of Bash’s syntax, where my code wasn’t doing what I thought it was doing. I might forget which terms need quoting in a condition like [[ $x -eq "$y" ]], or where I can and can’t omit the $ before a variable in an expression like $(( x = y )). I tried to keep the rules straight, but there were too many to absorb at once and it felt hopeless, until I discovered ShellCheck.

ShellCheck is a static analysis tool/linter for Bash scripts, and it is invaluable. I use it in VS Code (extension) and run it in CI. It flags cases where your code might not be doing what you expect, with links to wiki pages explaining the problem and potential alternatives.

Most of my recent Bash learnings have started with a ShellCheck warning code making me aware of an edge case or capability that I hadn’t considered. Like any linter, you may occasionally need to ignore its warnings with an annotation like # shellcheck disable=SC2034, but I’ve found its advice is usually very good, even when it seemed counterintuitive at first.

Even with ShellCheck, there are still some subtle cases where you can silence errors without realizing it, but not many.

Subshells

A lot of things about Bash have surprised me, but this was the most shocking: when you use parentheses to group commands, Bash forks the entire process to create a «subshell» child process running the parenthesized code!

(false || true || echo this is a subshell) && ls

echo "$(ls also-this)" "$(ls this-too)"

my_function() (
  echo this is a subshell
)

other_function() {
  echo but this is NOT, because I used curly braces instead of round parentheses
}

Enter fullscreen mode

Exit fullscreen mode

This is why if you try to set a global variable from inside of parentheses, the change won’t be visible outside: you’re only setting the value in the child process.

declare x=first
(x=second)
echo "$x"  # echoes "first"

Enter fullscreen mode

Exit fullscreen mode

This usually doesn’t cause a problem for error handling—our settings are propagated to the subshell, and the exit status of the subshell is propagated back. However, there is one major exception…

The unfortunate case of command substitution

Even with every available setting enabled, failures in command substitution subshells are usually silenced/masked and do not cause a failure in the original shell process. For example:

set -euo pipefail
shopt -s inherit_errexit

echo "$(
  echo >&2 "error: everything is broken"
  exit 66
)"

echo "but this is still running"

Enter fullscreen mode

Exit fullscreen mode

error: everything is broken
but this is still running

Enter fullscreen mode

Exit fullscreen mode

As far as I can tell, there is no way to change this behaviour, and ShellCheck can’t warn about it. If there is some way I’ve missed, please let me know! There are workarounds, but they’re clunky.

The exit status of these subshells is returned to the parent shell, however, it’s never checked before it is overwritten by the return status of the original command (echo in the case above). If we put the command substitution in an assignment expression on its own, instead of as an argument to another command, the exit status won’t be overwritten. For example:

set -euo pipefail
shopt -s inherit_errexit

declare output
output="$(
  echo >&2 "error: everything is broken"
  exit 66
)"
echo "$output"

Enter fullscreen mode

Exit fullscreen mode

error: everything is broken

Enter fullscreen mode

Exit fullscreen mode

This will behave properly, with the failure in output="$(...)" exiting the script.

Bonus suggestions

The default handling of glob expressions in Bash is confusing. Consider the command

ls ./builds/bin-*/

Enter fullscreen mode

Exit fullscreen mode

If builds contains one or more directories whose names start with bin-, you’ll get an argument for each, expanding to something like this:

ls ./builds/bin-windows/ ./builds/bin-linux/

Enter fullscreen mode

Exit fullscreen mode

However, if there’s no match, the glob expression isn’t replaced, it’s just passed to the command as-is, typically producing an error or unexpected behaviour:

ls: cannot access './builds/bin-*/': No such file or directory

Enter fullscreen mode

Exit fullscreen mode

There are two more-reasonable alternative behaviours, and I strongly suggest you set one of them: shopt -s nullglob will replace the glob expression with the empty string if it doesn’t match, and shopt -s failglob will raise an error.

Finally, you should set shopt -s compat"${BASH_COMPAT=42}" with the minimum Bash version you want to support, to reduce the chance of breakage in later versions. For Linux I usually target 42 (February 2011) but macOS only ships with 32 (October 2006).

Conclusion

Writing robust Bash scripts is tricky, but not impossible. Start your scripts with set -euo pipefail; shopt -s inherit_errexit nullglob compat"${BASH_COMPAT=42}" and use ShellCheck, and you’ll be 90% of the way there!

Appendix 1: Bash manual description of the -e/-o errexit setting

Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound 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 following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return value is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.

source: { COLUMNS=2048 man bash | grep -Em1 -A32 '^s+set [' | grep -Em1 -A32 '^s+-es{4}' | grep -Em2 -B32 '^s+-.s{4}' | sed '$d' | grep -EoA32 's{4}(Ss{0,4})+$' | grep -Eo 'S.*$' | fmt -tw$COLUMNS; }

10 More Discussions You Might Find Interesting

1. UNIX for Beginners Questions & Answers

BASH join command error PLS

i’ve tried every variation possible and keep getting not sorted error. can anyone shed any light on how to do this?

(image attached) (1 Reply)

Discussion started by: deadcick

2. UNIX for Beginners Questions & Answers

How to ignore Case with in COMM command?

Hi,

How can i ignore case between 2 files in unix using COMM command.
2 input files are:
-bash-4.1$ more x2.txt
HELLO
hi
HI
raj

-bash-4.1$ more x3.txt
hello
hi
raj

COMM command:
-bash-4.1$ comm x2.txt x3.txt
hello
HELLO
hi (3 Replies)

Discussion started by: raju2016

3. Shell Programming and Scripting

Ignore exit status for #!/bin/bash -e

I have a file /u/setuplink.txt

more setuplink.txt
ln -s /u/force.sh stopf.sh
ln -s /u/tnohup.sh tnohup.sh
ln -s /u/quick.sh startquick.sh
more runstat.sh
#!/bin/bash -e
echo «START»
/u/setuplink.txt
echo «END»
i wish to exit the runstat.sh for any errors except if the link… (2 Replies)

Discussion started by: mohtashims

4. UNIX for Beginners Questions & Answers

Find command with Ignore Access issues

Hi,

I am using following command to find a specific file.

find . -name «find*.txt» -type f -print

I am issuing that command at root directory since I don’t know in which sub folder that file is getting created from some other process.

As I am not having access to all directories, my… (3 Replies)

Discussion started by: RameshCh

5. Shell Programming and Scripting

Bash script with awk command ERROR

Hello im new here… Im trying to read file and create folders from words in it but i get this for loop error
awk : line 3 : syntax error at or near for

my code is..

#!/bin/bash
begin
for (( i=1;i<=5;i++));
do
awk -v i=$i $0 { print $i }
mkdir $i
done
{print $i}
end {}

i have… (7 Replies)

Discussion started by: boxstep

6. Shell Programming and Scripting

How write the ignore dupkey command in UNIX?

I know that in oracle the is a way to write to ignore the dupkey, something like

/*+ ignore_row_on_dupkey_index(tab, index_tab) */

Is there a way to do the same thing but with unix commands? I think there’s a way with WHENEVER SQLERROR EXIT SQL.SQLCODE but i’m not sure and i don’t know how do… (3 Replies)

Discussion started by: punticci

7. Shell Programming and Scripting

Find command with ignore directory

Dear All,

I am using find command
find /my_rep/*/RKYPROOF/*/*/WDM/HOME_INT/PWD_DATA -name rk*myguidelines*.pdf -print

The problem i am facing here is find /my_rep/*/
the directory after my_rep could be mice001, mice002 and mice001_PO, mice002_PO

i want to ignore mice***_PO directory… (3 Replies)

Discussion started by: yadavricky

8. Shell Programming and Scripting

bash syntax error: command not found

I am trying to create a shell that asks the user to enter their name, and compare it to my own by saying we have the same name or saying my name and that they have a nice name too. Here is my script…

#!/bin/bash-x
echo «Enter your name».
read name
if
then
echo «My name is Adam too»… (1 Reply)

Discussion started by: amaxey45

9. Shell Programming and Scripting

#!/bin/bash and #1bin/sh command not found error on mac osx terminal/shell script

i am having a weird error on mac os x running some shell scripts. i am a complete newbie at this and this question concerns 2 scripts. one of which a friend of mine wrote (videochecker.sh) a couple weeks ago and it’s been running fine on another machine.

then last week i wrote capture.sh and it… (2 Replies)

Discussion started by: danpaluska

10. Ubuntu

set completion-ignore-case on doesn’t work in bash

completion-ignore-case option doesn’t work in my version:

/home/user $ echo $BASH_VERSION
3.2.48(1)-release
/home/user $ ls -l *
-rw-r—r— 1 user user 0 2009-10-18 00:09 somefile
-rw-r—r— 1 user user 0 2009-10-18 00:09 Somefile
/home/user $ set completion-ignore-case on

But when I… (2 Replies)

Discussion started by: Sapfeer

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