views:

297

answers:

5

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.

+1  A: 

<sarcasm>Pitfall #1: Using BASH for programming (instead of Perl).</sarcasm>

DVK
If you call that things that comes out from Perl programs, then yes use Perl. Personally I would write programs in Ruby. But to automate simple tasks that do not require a couple of dozen lines of code I would definitely use BASH.
Alexander Pogrebnyak
Shell is still a preferred language for scripts that will reside in /etc/init.d, and for anything that might need to be run in single user mode. In addition, if 'system' calls are made often shell might be a good choice. And, I'd use Python for anything 'interesting.'
semiuseless
@Alexander - when a real programmer programs in pretty much any language (Korn shell possibly excluded), what comes out is programs. the difference is ease of coding for said real programmer. I can average 2x-10x faster coding in Perl vs. Shell script for anything more complicated than 5 system calls in a row, and I'm fairly proficient in both.
DVK
+1  A: 

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.

Alexander Pogrebnyak
+3  A: 

Pitfalls...Not leave a space between [ ] and its condition.

Hai
+3  A: 

Bash Pitfalls

One of the most common pitfalls is getting quoting wrong.

Also, never parse ls.

Another: not using the full range of features, including:

More information.

Dennis Williamson
A: 

There 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
semiuseless
It's also important to know that pipes start subshells: http://mywiki.wooledge.org/BashFAQ/024
Dennis Williamson
@Dennis: Pipes are certainly one of the subshell pitfalls that beginners face when learning to write (and understand) shell code. Thanks for adding that.
semiuseless