Everything we’ve covered about Bash up to now has really been Bash scripting in disguise. However, all that you’ve really seen is how to run single lines of code. While some of these single lines are really powerful, we can do better.
Bash is a programming language like any other, and as such we can create files that contain Bash programs. We call these files “scripts” and the practice of creating them “scripting.”
The general syntax of a Bash if statement is
if <condition>; then
...
fi
# OR
if <condition>; then
...
else
...
fi
The condition is sort of tricky; Bash doesn’t have boolean values like true and false. Instead, it has commands, and every command that you run in the shell has to return a number. When programs run to completion without an error, they return 0. When an error occurs, they return an error code, which is usually 1, -1, or some other number that corresponds to the type of error that occurred.
For the most part, we ignore this and coerce Bash into behaving as if it had
booleans. To do this, we use the test
program, which is abbreviated as [
.
For a comprehensive reference, see man test
. To see how it works though,
let’s see some examples:
# check if variable equals a string
if [ "$myvar" = "something" ]; ...
# check if variable doesn't equal a string
if [ "$myvar" != "something" ]; ...
# test if a file exists and is a regular file
if [ -f "./myfile" ]; ...
# test if a directory exists
if [ -d "./myfile" ]; ...
# test if a file exists (of any type)
if [ -e "./myfile" ]; ...
# test if a string is the empty string (length zero)
if [ -z "$myvar" ]; ...
# test if either of two conditions are true (logical or)
if [ -z "$myvar" -o "$myvar" = "something" ]; ...
Again, for a complete list of flags you can use, check out man test
.
We’ll just be talking about for
loops here, even though Bash also has while
loops. While loops come up in a few programs, but for loops are far more
common. Syntax:
for myvar in <items>; do
...
# can access current item with $myvar
...
done
~~~
where <items>
is a space-delimitted string. What happens is that myvar
iteratively takes on the next “word” contained in <items>
. <items>
can be
a valid Bash expressions, so you can loop over things like the contents of a
variable, a Bash glob, or the result of a command substitution.
# .
# ├── colors
# │ ├── blue
# │ ├── green
# │ ├── red
# │ └── white
# └── seasons
# ├── fall
# ├── spring
# ├── summer
# └── winter
# using a bash glob (bash globs exapand to space-separated strings)
for file in *; do
echo $file
done
# output:
# colors
# seasons
# using command substitution (the dictionary is newline-separated)
for word in $(cat /usr/share/dict/words); do
echo "Current word: $word"
done
# output:
# Current word: A
# Current word: a
# Current word: aa
# Current word: aal
# Current word: aalii
# ...
# using contents of a variable
seasons="fall winter spring summer"
for season in $seasons; do
echo $season
done
# output:
# fall
# winter
# spring
# summer
Once we get a collection of lines together, it’s handy to put them in a file so that we can run them repeatedly. There are very few parts to a Bash script file. Let’s look at an example:
#!/usr/bin/env bash
first_argument="$1"
if [ "$first_argument" = "hello" ]; then
echo 'Hello!'
else
echo "Aww, you didn't say hi to me..."
fi
First, notice the top line. This is what’s called a shebang, and is basically just a line of code that tells the shell what program we want to run the contents of this file. We’re writing a script in Bash, so we chose Bash. (We could have just as easily been writing a Python script and changed “bash” to “python”).
The rest of the file is just a sequence of Bash commands! That means we can do things like assign to variables (line 3) and include if statements (line 5).
Note that if you saved this file as “my-first-script.sh” and tried to run it
(./my-first-script.sh
), you’d get an error. If you remember back to Running
Executables, we need to use chmod
to give this file executable
permissions:
chmod +x my-first-script.sh
Notice that we used a special variable $1
in the previous command. We didn’t
set it; rather, it was set for us before our Bash program started. It contains
the first argument that the user specified at the command line after typing the
command itself. Thus, we have to run
./my-first-script.sh hello
in order for $first_argument
to equal “hello” and get the program to say hi to
us.
For more about positional parameters, check out this page.
Bash is a very intricate and powerful language. It’s much too large to be covered in full here; indeed, we’ve only scratched the surface. If you’re looking for a good resource to read cover-to-cover, check out the tutorial by The Linux Documentation Project.
Other than that, just be curious. A lot of the time, just recognizing that a particular Bash command would be handy to help you solve a problem is enough to form the beginnings of a Bash script. From there, well-formulated questions and Google searches will get you the rest of the way there.
Copyright © 2014, Great Practical Ideas in Computer Science.