With bash scripts, you can run sequences of commands. They prevent you from repeating yourself and allow you to store long and tedious to type commands for later usage. In this article, you’ll learn how to create a Bash script. You’ll also learn how to read arguments, use variables, and create for-loops to repeat actions.
Before you start creating scripts, make sure you are familiar with the basic Linux commands.
Table of Contents
Create a Bash script
A command-line script starts with the shebang — a sequence of the two characters #!
. After these two characters, you can name any interpreter and a bunch of arguments, like this:
#!interpreter [optional arguments]
A shell script is usually based on the most popular shell called Bash, so this would simply become:
#!/bin/bash
Sometimes you’ll see this instead:
#!/usr/bin/env bash
When using /usr/bin/env
, the provided command will be searched for in the current $PATH
, instead of hardcoding it to the /bin/bash
location. Bash will almost always be found at /bin/bash
, so the first method is fine for us.
However, you may want to use this trick to create an executable Python script. Python can be installed anywhere, depending on the way it is installed. I’ve seen it in /usr/bin/
, /bin
, and /usr/local/bin
, just to name a few. If you’re running a virtual environment, it can be anywhere. So searching the $PATH
is a good option, since that will find the Python version from the virtual environment.
We’ll get to creating an actual shell script in a moment. But first, we’ll take a look at variables and for-loops to juice things up a little.
Bash variables
Just like other languages, Bash has variables. One peculiar thing about variables in Bash is that you access them with the dollar sign, but you set them without it. To clarify, here’s an example:
myvar="Hello world" echo $myvar
Bash has a number of reserved variables, that are automatically set for you. The following table is a non-exhaustive list of some of the more useful ones:
Variable | Function |
---|---|
$0 | Name of the current script. |
$1 … $9 | First 9 arguments to the script. |
$# | Number of arguments passed to the script. |
$@ | All the arguments supplied to the script. |
$USER | The username of the user running the script. |
$HOSTNAME | The hostname of the machine the script is running on. |
$SECONDS | The number of seconds since the script was started. |
$RANDOM | Returns a different random number each time is it referred to. |
$LINENO | Returns the current line number in the Bash script. |
Bash Script arguments
We’re now able to create a shell script, but this script will do the same thing each time you run it. We can make it infinitely more interesting by using arguments.
Here’s an example, call it arguments.sh
:
#!/bin/bash echo "Hello $1, from $0"
If we run this with one argument, the result is as follows:
$ ./arguments.sh Weirdo Hello Weirdo, from ./arguments.sh
What Bash does, is chop up your entire command and assign it to the not very imaginative variables $0
, $1
, $2
, et cetera. As you can see, the first part of our command is the name of the script itself. This can come in handy, as can be seen in the next section.
Create a safety net
Before moving on to the serious stuff, I want to teach you about an important safety net.
Once you start creating scripts, you will inevitably make mistakes. We all do. One very common mistake in scripts can be especially disastrous. It’s the unset variable. If you don’t know what I mean, just keep reading as it will become clear in a moment.
We can prevent using unset variables by starting our scripts with the following command:
set -u
That’s all! From here on, your script is treating unset variables as an error when you are substituting.
For example, this script will fail:
#!/bin/bash set -u rm -rf /$my_directory
The output:
$ ./test.sh ./test.sh: 3: ./test.sh: my_directory: parameter not set
That’s because I never set $my_directory
. Without set -u
, it won’t fail but instead just ignore the empty variable, leaving us with the command rm -rf /
. We all know what that does, right? If executed by a privileged user (like root
), this mistake would have wiped my entire filesystem instead of the directory I intended to delete.
Make a habit of always starting your scripts with set -u
.
Bash for loop
The usefulness of the command-line, and especially scripts, increases exponentially once you master Bash for-loops. And even though they might look scary, they are really not that hard.
Syntax of a Bash for-loop
The basic syntax of a loop is:
for VARIABLE in A LIST do command1 command2 commandN done
Example Bash script
The A LIST
part can be anything: file names, numbers, or strings. It will be a list of file names in most scripts, though, since that’s what we’re often working with in Bash. Now that we know how to create a loop, let’s look at our first script:
#!/bin/bash echo "You can list numbers and text like this:" for n in 1 2 3 four do echo "Number $n" done echo "Or specify a range of numbers:" for n in {1..5} do echo "Number $n" done echo "Or use the output of another command:" for f in $(ls) do echo $f done
In the last for-loop, I used the expression $(ls)
. This executes the command between parentheses and substitutes the result. In this case, ls
gets executed and the for-loop is fed with the file names that ls
prints out.
Starting a Bash script
To start this script, we can do two things. First, we can run it with:
$ bash loop.sh
The second way, that I recommend, is making the file executable. The OS will know how to execute our file because of our shebang line at the top! Making the file executable is done by setting the file’s “execute flag” like this:
$ chmod +x loop.sh
You can now run the script with:
$ ./loop.sh
The output in my case is:
$ ./loop.sh You can just list a bunch of numbers and text like this: 1 2 3 four Or specify a range of numbers: 1 2 3 4 5 Or use the output of another command: loop.sh notes.txt
It might differ for you, depending on which files are in the directory that you’re running the script in.
Bash conditional programming
Sometimes you only want to run a command if a certain condition is true. For this, we have the if… then… else…fi
construct in bash.
Check arguments in bash script
We can use conditional programming to improve our previous example, arguments.sh
, since it contained a little problem. It expects a name in $1
without checking if it actually gets one. Let’s fix this:
If that didn’t work, here’s a static version of the same code:
#!/bin/bash if test -z "$1" then echo "Usage: $0 <Your name>" else echo "Hello $1, from $0" fi
The test command in bash
With test -z
we can check if a variable’s length is zero. If that is the case, we print some friendly usage instructions:
$ ./arguments.sh Usage: ./arguments.sh <Your name>
The test command can test for many things so it really aids in conditional programming. The full list can be seen when you enter man test
on the command line. That’s right, you don’t need Google for everything! Using man-pages is part of being a command-line ninja! You’ll find that there’s a man page for pretty much everything you can do in your terminal. After all, in the old days, we didn’t have the Internet to google everything…
Here is one more example in which we compare two values to see if they are the same:
Here’s the static version in case that didn’t work:
#!/bin/bash for i in {1..10} do if test $i -eq 3 then echo "I found the 3!" else echo "Not looking for the $i" fi done
This loop runs for 10 iterations and checks if $i
is equal to 3 each time. Can you predict the output?
Although the else
-part is optional, you always need to end with a fi
.