Committing routine and even rarely required tasks to scripts is almost always a big win because you don’t have to reinvent the approach to getting work done each time it’s needed, and you save a lot of time on issues you handle often.
Here are some tips for writing bash scripts and ensuring that they’ll be easy to use, easy to update/ and hard to misuse.
Comments
One important thing to do when you’re preparing a script on Linux is to add comments – especially for commands that might be a little complex. If you don’t run a script very often, comments can help ensure that you quickly grasp everything that it’s doing. If someone else has to use your scripts, the comments can make it a lot easier for them to know what to expect. So, always add comments. Even you might appreciate them! You don’t need to comment every line, just every significant group of commands. Here’s a simple example.
#!/bin/bash # function to replace spaces in provided text with underscores replspaces () { echo $@ | sed 's/ /_/g' } # function to remove spaces from the provided text dropspaces () { echo $@ | sed 's/ //g'; } # commands to call the functions defined above replspaces "Hello World" replspaces `date` dropspaces "Hello World" dropspaces `date`
The script above defines two functions: one to turn spaces into underscores and one to remove spaces altogether in whatever string is provided at the prompt. The comments say enough to explain this.
Using functions
As noted in the script above, functions can come in handy. This is especially true when a single script will run the functions many times. The scripts will be shorter and will be easier to understand.
Verifying arguments
Make a habit of having your scripts verify that the proper arguments have been provided by whomever runs them. You can check the number of arguments, but you can also verify that the responses are valid. For example, if you prompt for the name of a file, check that the file provided actually exists before running commands that attempt to use it. If it doesn’t exist, respond with an error and exit.
The script below checks to ensure that two arguments have been provided as arguments to the script. If not, it prompts for the information needed.
#!/bin/bash if [ $# -lt 2 ]; then echo "Usage: $0 lines filename" exit 1 else numlines=$1 filename=$2 fi …
The script below prompts for a filename and then checks to see if the file exists.
#!/bin/bash # get name of file to be read echo -n "filename> " read $filename # check that file exists or exit if [ ! -f $filename ]; then echo "No such file: $filename" exit fi
If needed, you can also check whether the file is writable or readable. The version of the script below does both.
#!/bin/bash # get name of file to be read echo -n "filename> " read filename # check that file exists or exit if [ ! -f $filename ]; then echo "No such file: $filename" exit else # determine if file is readable if [ ! -r $filename ]; then echo "$filename is not writable" exit fi
# determine if file is writable if [ ! -w $filename ]; then echo "$filename is not readable" exit fi fi
Note: The -r $filename check asks whether the file is readable. The ! -r $filename check asks whether it’s not readable.
Exiting on errors
You should almost always exit when an error is encountered when a script is run. Explain with an error message what went wrong. The set -o errexit will cause a script to exit regardless of what kind of error is encountered.
#!/bin/bash set -o errexit # exit on ANY error tail NoSuchFile # tries to show bottom lines in NON-EXISTENT file echo -n "Enter text to be appended> " read txt echo $txt >> NoSuchFile
The script above will exit if the “NoSuchFile” file doesn’t exist. An error will be displayed as well.
To exit when a variable hasn’t been assigned a value (i.e., doesn’t exist), use the nounset option as shown in the script below. A set -u command works the same.
#!/bin/bash # exit script if an unset variable is used set -o nounset # welcome user with a greeting echo $greeting echo -n "Enter name of file to be evaluated: " read filename
If you run the script as is, you’ll run into the issue shown below and never be prompted for the file to be processed:
$ ck_nounset
./ck_nounset: line 7: greeting: unbound variable
Working with quotes
In a lot of cases, it doesn’t matter if you use single quotes, double quotes, or no quotes at all. Putting quotes around a variable name in an if statement means that you won’t run into an error if the variable is not defined. The shell will never end up trying to evaluate the invalid command if [ = 2 ]. The command if [ “” = 2 ] is valid.
if [ "$var" = 2 ]
Putting quotes around arguments with more than one string is essential. You’d run into problems with the script below if the user answering the good/bad question entered “very good” instead of just “good” or “bad”.
#!/bin/bash echo -n "Did you find the argument to be good or bad?> " read ans if [ $ans = good ]; then echo "Thank you for your feedback" else echo -n "What didn't you like?> " read ans echo $ans >> bad_feedback fi
Instead, you could use a line like this to do the comparison. This would allow either “good” or “very good” as a response.
if [[ "$ans" = *"good" ]]; then
Using = or ==
Most of the time it makes no difference whether you use = or == to compare strings. However, some shells (e.g., dash) don’t work with ==, so save yourself the keystrokes and stick with = if you’re not sure.
File permissions
Make sure your scripts can be run by anyone who should be able to use them. A chmod -ug+rx filename command would give you (presumably the file owner) and anyone in the group associated with the file the ability to read and run the script.
Test your scripts
Don’t forget to spend some time testing to make sure your scripts do exactly what you intend. Testing can include things like failing to respond to the script’s prompts to see how it reacts. (2:15)
Wrap-Up
Theres more advice about building reliable and easily maintainable scripts including learning to script on Linux using bash.