What are the rules to write robust shell scripts?

robustness, shell

Solution

A couple of ideas:

Use `-e` flag in the shebang, for example `#!/bin/sh -e`. That way the script will stop at the first error. It's a bit like throwing a `RuntimeException` in Java. This probably did save my ass a couple of times, and I have a feeling it would helped you too in this case.

Handle the exit codes of all the statements in your scripts. Actually using `-e` in the shebang will force you to do this.

Don't chain commands with `;`. Use `&&` instead. Again, using `-e` in the shebang will force you to do this.

Properly quote paths that might contain spaces or other special characters.

It's best if a script doesn't do dangerous things when used without parameters.

For non-trivial scripts, make sure to `-h` and `--help` flags that print a helpful message. (I use this script to generate scripts with flag parsing.) This should be mandatory for scripts that can do dangerous things when called without parameters.

Exit your scripts with an explicit `exit 1` on any non-normal exit. It's a common mistake to handle an error in an `if` block, `echo` some helpful message, and then `exit` without any arguments. Since `exit` uses the exit code of the last command, in this case `echo`, it will exit with success. Note that in the sample script I linked earlier, I `exit 1` after printing the help message when handling the `--help` flag.

If you don't need `bash` features, use a `#!/bin/sh` shebang and try to stay compatible with older versions. Being portable is a kind of robustness, I think.

Use `$()` instead of


Format your code nicely and consistently. Easier to read ~~ robustness.

Be aware of the differences between platforms. For example both `date --iso` and `date +%F` print dates in the format `2013-11-17`, but the first only works in GNU systems, the second works in BSD and Solaris too. So use `date +%F` always, it works everywhere. There are of course 100s of examples like this. If you're doing something you're not used to every day, try to check if it works in a different system too.

Test for empty variables, especially in dangerous commands like `rm -fr`. For example the result of `rm -rf "$TOPDIR/$OBJDIR"` can be disastrous if TOPDIR or/and OBJDIR happens to be empty or unset. In general, double-check or triple check the possible parameter values of dangerous commands like this.

Don't push the limits of shell scripts. Scripts are supposed to be glue-code. If you catch yourself doing something tricky, or that you need obscure features, chances are you're better off moving to more powerful languages.

In the end, none of this will prevent you from making stupid mistakes.

PS: I will keep coming back and add more things as I remember them. Please feel free to suggest improvements and I'll add them. Thanks.

Problem

I recently erased part of my home directory with a shell script I wrote. Fortunately, I did hit Ctrl-C fast enough to avoid the worst. My mistake has been to rely too much on relative paths. Since now, I always use absolute paths when changing directory... But, it lead me to an interesting question: What are the rules to write robust shell scripts ? I already know that one should always use absolute paths when moving to from a directory to another. But, there must be plenty others (for example, what to do when a command fail, what is a nice way to stop the script and inform the user about it). So, does anyone have a list of such a rules and hints specifically targeted for shell scripts?

Original source