Every time a Bash command is executed, it returns an exit status. This exit status tells whether the command was successful or not. By convention, an exit status of 0 means success, while any non-zero exit status means an error.
You can check the exit status of the last executed command using the $? variable. For example:
$ ls
file1 file2
$ echo $?
0
$ ls non_existent_file
ls: cannot access 'non_existent_file': No such file or directory
$ echo $?
1
Here the first ls
command succeeded and returned 0, while the second ls
failed and returned 1.
You can use this exit status to handle errors in Bash scripts. For example:
#!/bin/bash
ls some_file
status=$?
if [ $status -ne 0 ]; then
echo "Error occurred!"
exit 1 # Exit script with error code 1
fi
echo "Command succeeded"
Here we check the exit status of ls
and if it's not 0, we print an error message and exit the script with a non-zero error code.
This allows Bash scripts to handle errors gracefully and take appropriate actions.
So in summary, Bash exit statuses allow you to check if a command succeeded or failed, and take necessary actions based on that in your Bash scripts.
Best practice
Here are some best practices for error handling in Bash scripts:
Always check the exit status of commands using $?. This is the most important step to catching errors.
Use if/else conditions to check for non-zero exit statuses and take appropriate actions:
if [ $? -ne 0 ]; then
# Command failed, handle error...
else
# Command succeeded
fi
- Exit the script with a non-zero exit code on error. This tells the calling process that the script failed.
exit 1 # Exit with error code 1
- Use meaningful error messages to inform the user about what went wrong.
echo "Error accessing file $filename"
For critical errors, it's a good idea to stop script execution immediately. You can use
exit 1
for this.Use try/catch blocks around critical sections of code to handle errors in a grouped manner.
try {
# Code that may fail...
} catch {
# Handle error
}
Log errors to a file for debugging. This can help track down intermittent issues.
Consider retrying commands that fail temporarily (e.g. due to network issues). But limit the number of retries.
Define custom functions to handle common errors in a reusable manner.
Test your scripts thoroughly and try to "break" them to ensure error handling works as expected.
So in short, the main best practices are to check exit statuses, use if/else conditions, exit appropriately, give meaningful error messages, stop execution on critical errors, log errors, retry temporarily failed commands, define reusable functions and thoroughly test your error handling.
Logging Errors
Here is how you can log errors and warning messages into a file from a Bash script:
- Define the log file path:
LOG_FILE=/var/log/myscript.log
- Open the log file in append mode:
exec 3>>$LOG_FILE # File descriptor 3 points to the log file
- Use
printf
to log messages to the log file:
printf "%s - %s\n" "$(date)" "Error connecting to server" >&3
Here:
%s
is a format specifier>&3
redirects the output to file descriptor 3 which points to the log file
- You can log error messages based on the exit status:
command
if [ $? -ne 0 ]; then
printf "%s - %s failed\n" "$(date)" "$command" >&3
fi
- Close the log file at the end:
exec 3>&- # Close file descriptor 3
- You can also log debug/info messages conditionally using a log level:
LOG_LEVEL=2
if [ $LOG_LEVEL -ge 2 ]; then
printf "%s - Debug message\n" "$(date)" >&3
fi
So in summary, you define a log file, open it using a file descriptor, use printf
and >&3
to log to that file descriptor, log based on exit statuses, and close the file at the end. Using a log level allows you to log more verbose debug messages conditionally.
Disclaim: I learn programming using AI to make money. If you find this useful tell me in comments and sponsor me to write more. Life is very short. Learn fast and prosper. ๐