Updated July 2020: I added a Section about Shell Script Headers.
It is easy to write bad shell scripts, and there is a lot of room for mistakes!, possibly the best tip one could give is to use a higher-level language, like python, and try to avoid writing shell scripts at all. But if you just have to, or want to, here are some helpful Tips/Resources you can use to write better shell scripts ;)
Use the Google Shell Style Guide
Using a consistent coding style among your Team/Company is important, the same applies to shell scripts as well! Try to find out if there already exists a guideline within your Structure, and adopt it.
If there is none in place, you should take a look at Googles Shell Style Guide which is a really great reference! (Maybe also check out their other Style Guides)
Use ShellCheck for linting
While using Linters with "real" programming languages, like python, is the default and most specialized IDE's have linting functionality neatly integrated, most people don’t think about linting their bash scripts, as they are just considered to be "quick hacks or whatever".
Nevertheless it comes as a surprise that there also exist linters for bash and ShellCheck is a really good one!
You can easily install it locally and/or try it on-line by visiting www.shellcheck.net (don't blindly copy and paste your secrets into that, or any other website ;) )
Most modern Text editors like Sublime or Atom also have a great ShellCheck integration, give it a try!
Use Shell settings to increase script safety
If a command within your shell script goes wrong, a variable is not set or a piped command errors, the default behavior of bash is to, more or less, silently fail and go on with the script, as if nothing happened. This can be rather dangerous, and often results in unintended script behavior.
To increase script safety and avoid unintended behavior, you must explicitly tell your shell to automatically exit when an error occurs!
This can be done by setting
set -eu -o pipefail at the beginning of your script you tell bash to stop and exit immediately when a Step fails!
Read more about writing safe shell scripts, over here https://sipb.mit.edu/doc/safe-shell/
Put a descriptive Header at the Start of your script
Even though Shellscripts are often intended as "throwaway" Solution or a "dirty quick fix" They more often than not, become permanent fixtures in critical Systems!
By providing a descriptive Header with additional context at the start of your shell script, you can easily improve the lives of anyone stumbling upon your "quick fix", possibly years later.
Especially adding the
author Information helps track down the Person that knows what's up, when needed.
i.E. this is the example header for a fictive Fish feeding shell script. (imagine a robotic fish feeder controlled via an http API. 🤖🐠🤷, No fish have been harmed!)
#!/bin/bash #title :feed_the_fish.sh #description :This script feeds the Fish, via controlling the feed-the-fish.com API. #author :firstname.lastname@example.org #date :2017-05-20 #version :0.2.1 #usage :bash feed_the_fish.sh #notes :This script depends on curl to call the fish API. #bash_version :4.2.1(1)-release
Having such a Header is a nice way keep a track of what your script does, when it was created, the author of the script, etc..
Update: how to write
idempotent Shell Scripts:
I Recently found this Blog Post, and I think it is a great addition to this topic!
Beware though that some of the Actions described in the Blog Post are NOT truly idempotent, as i.E.
touch will alter the modification time of the file, and forcefully creating/recreating/deleting things are changes! But the Ideas and Concepts conveyed in this Article are good non the less, and it is an interesting Read!
You still should use a Tool like Ansible or another truly idempotent Configuration Management Solution in most cases anyway tough!
Update: Use explainshell.com for explanation on what your Shell Script does.
references and further reading material