I am not able to find this particular discussion in SO. What are all the common mistakes and pitfalls in the BASH programming / shell scripting?
PS: Please close if this has been discussed already.
I am not able to find this particular discussion in SO. What are all the common mistakes and pitfalls in the BASH programming / shell scripting?
PS: Please close if this has been discussed already.
<sarcasm>Pitfall #1: Using BASH for programming (instead of Perl).</sarcasm>
For BASH programming I would say the most common pitfall is mistyping shell variable name and doing something in root /
directory. E.g
test_dir=/test
rm -rf $tset_dir/*
There is a simple remedy for this. You should always use :?error string
parameter expansion when referencing shell variables. Doing this terminates scripts wherever it encounters a variable that has not been defined. I usually always use ${var_name:?nd}
expansion to cut down on typing ( nd for Not Defined ). Rewriting the previous example:
test_dir=/test
rm -rf ${tset_dir:?nd}/*
The BASH interpreter will generate an error and terminate your script, thus preserving your system.
Other advise, try to terminate a script as soon as you hit an error condition. For this put this as a first line of your script file: #!/bin/bash -e
This will terminate your script when any command in your script file will return non-0 code.
One of the most common pitfalls is getting quoting wrong.
Also, never parse ls
.
Another: not using the full range of features, including:
[[ ]]
over [ ]
$()
over backticksThere are several pitfalls that I commonly see, some are specific to shell programming, and some are more general to programming in any language.
Not understanding when child shells are started is a common problem. For instance, invoking a shell script that will set an environment variable and then exit. That environment variable will not be set in the parent shell.
In bash, the "dot" notation is used to invoke an external script and have it execute in the current shell process. This technique can be used to set environmental variables and aliases in the current shell, or as a way to "include" files into a shell script.
. ~/setup_my_environment.sh
EDIT: One of the pitfalls of shell is the lack of a good debugger. A quick way to get a syntax check is to put new code into a function and "dot" that function into the current shell. Even if no code executes, there is a full parse of the code by the shell. This will not find logical errors, but does tend to catch some syntax errors, and most unbalanced delimiters - quotes, parens, and brackets. The last line of the history will hold the "line" of shell where the parser finally gave up...this can be helpful in identifying the actual location of the syntax error.
It is common to see constructs like this that rely on the default "success value is the same as true" behavior:
# Check for success
if [[ $some_status ]] ; then
do_something
fi
In most shell environments a return value of "0" indicates success, and a non zero return value indicates failure. This is not the convention for 'true' and 'false' that is used in many other languages. Occasionally this can cause issues. I find that for programmers who will work in multiple languages, and explicit check against a value is helpful:
# Check for success
if [[ $some_status == 0 ]] ; then
do_something
fi
# Do more stuff
# Check for any failures
if [[ $some_status != 0 ]] ; then
handle_error
fi