var=f1
if [ -e $var ]
then
echo "$var exists"
fi
Control flow
By default, scripts get executed linearly from top to bottom. Often however, you want to control what gets executed when.
This section covers various ways to control the flow of execution through a script.
Normal execution of commands
Commands get executed from top to bottom and from left to right. Different commands are separated by a line break and/or a semi-colon.
Example:
Look at the following commands:
unzip bash.zip
rm bash.zip
This is equivalent to:
unzip bash.zip;
rm bash.zip
and to:
unzip bash.zip; rm bash.zip
This is what we did to get the data for the past few sessions.
In all three cases, both commands will try to run. Now, if for some reason, the unzipping fails, the zip file still gets deleted. That’s a bummer.
Conditional on previous command
Execution conditional on success
Commands can be limited to running only if the previous command ran successfully thanks to the double-ampersand (&&
).
Example:
unzip bash.zip &&
rm bash.zip
This is equivalent to:
unzip bash.zip && rm bash.zip
If the unzipping works (if it returns a zero exit status), then the Zip file gets deleted. If however, the unzipping fails (if it returns a non-zero exit status), the script aborts and we haven’t lost our Zip file.
Execution conditional on failure
The opposite of &&
is ||
: the second command only gets executed if the first one failed.
Example:
unzip bash.zip || echo "Unzipping failed"
This can also be written as:
unzip bash.zip ||
echo "Unzipping failed"
Conditional executions
Commands can be executed or not depending on some conditions. To achieve this, we first need to have expressions that define these conditions.
Predicates
Predicates are expressions that, when evaluated, return either true or false.
Here are examples of predicates:
[ $var == 'text' ]
checks whether var
is equal to 'text'
.
[ $var == number ]
checks whether var
is equal to number
.
[ -e name ]
checks whether name
exists.
[ -d name ]
checks whether name
is a directory.
[ -f name ]
checks whether name
is a file.
Make sure to have spaces around each bracket.
Your turn:
- Create a directory
d1
and a filef1
.
Write the predicates that test whether:
d1
exists,d1
is a file,d1
is a directory,f1
is a file,f1
is a directory.
If statements
Syntax
In its simplest form, if
statements look like:
if [ predicate ]
then
command1
command2
...
fi
This can also be written as:
if [ predicate ]; then command1; command2; ...; fi
If the condition is true, the commands are executed, if the condition is false, nothing happens.
Examples
var=f2
if [ -e $var ]
then
echo "$var exists"
fi
Your turn:
Write a conditional expression that prints “d1 is a directory” if d1
is a directory and test it.
If else statements
Syntax
If you want a different set of commands to be executed when the condition is false, you add an else
statement:
if [ predicate ]
then
command1
command2
...
else
command3
command4
...
fi
Examples
var=f1
if [ -e $var ]
then
echo "$var exists"
else
echo "$var does not exist"
fi
f1 does not exist
var=f2
if [ -e $var ]
then
echo "$var exists"
else
echo "$var does not exist"
fi
f2 does not exist
If elif else statements
Of course, you can have multiple conditions defining trees of if
statements. In that case, you use elif
(any number of times):
Syntax
if [ predicate1 ]
then
command1
command2
...
elif [ predicate2 ]
then
command3
command4
...
else
command5
command6
...
fi
Examples
var=4
if (( $var < 0 ))
then
echo "$var is negative"
elif (( $var > 0 ))
then
echo "$var is positive"
else
echo "$var is equal to zero"
fi
4 is positive
Your turn:
Play with the value of var
to test our if elif else statement.
Conditionally repeated executions
Commands can be executed as long as a condition returns True
thanks to while loops.
Syntax
The syntax of a while loop in Bash is:
while [ predicate ]
do
command1
command2
...
done
Example
var=0
while (($var<10))
do
echo "$var"
((var++))
done
0
1
2
3
4
5
6
7
8
9
Be careful that while loops can lead to infinite loops. Such loops need to be manually interrupted (by pressing <Ctrl+C>
).
Example of infinite loop:
var=1
while (($var>0))
do
echo "$var (Press <Ctrl+C> to stop)"
((var++))
sleep 1
done
Executions repeated over a collection
Commands can be repeated for each element of a list thanks to for loops.
Collections
For loops run a set of commands for each item of a collection. How do you create those collections?
Listing items one by one
The least efficient method is to list all the items one by one:
for i in file1 file2 file3
do
echo $i
done
file1
file2
file3
Wildcards
ls *.pdb
ls: cannot access '*.pdb': No such file or directory
Brace expansion
Collections can also be created with brace expansion.
Examples:
echo {1,2,5}
1 2 5
Make sure not to add a space after the commas.
echo {list,of,strings}
list of strings
echo {file1,file2}.sh
file1.sh file2.sh
ls {ethane,methane,pentane}.pdb
ls: cannot access 'ethane.pdb': No such file or directory
ls: cannot access 'methane.pdb': No such file or directory
ls: cannot access 'pentane.pdb': No such file or directory
echo {1..5}
1 2 3 4 5
echo {01..10}
01 02 03 04 05 06 07 08 09 10
echo {r..v}
r s t u v
echo {v..r}
v u t s r
echo {a..e}{1..3}
a1 a2 a3 b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3
echo {a..c}{a..c}
aa ab ac ba bb bc ca cb cc
echo {1..5}.txt
1.txt 2.txt 3.txt 4.txt 5.txt
echo file{3..6}.sh
file3.sh file4.sh file5.sh file6.sh
Sequences
Collections can also be sequences:
seq 1 2 10
1
3
5
7
9
Here, 1
is the start of the sequence, 10
is the end, and 2
is the step.
For loops
Syntax
The general structure of a for loop is as follows:
for iterable in collection
do
command1
command2
...
done
Examples
The molecules
directory contains a number of .pdb
files. We want to rename them by prepending “gas_” to their current names.
We can do this by creating a collection with a wildcard and applying the command to each element of the collection with a for loop:
for file in *.pdb
do
mv $file gas_$file
done
This can also be written as:
for file in *.pdb; do mv $file gas_$file; done
Here is a for loop using a collection created by a sequence:
for i in $(seq 1 2 10)
do
echo file$i.txt
done
file1.txt
file3.txt
file5.txt
file7.txt
file9.txt
Your turn:
In a directory the command ls
returns:
fructose.dat glucose.dat sucrose.dat maltose.txt
What would be the output of the following loop?
for datafile in *.dat
do
cat $datafile >> sugar.dat
done
All of the text from
fructose.dat
,glucose.dat
andsucrose.dat
would be concatenated and saved to a file calledsugar.dat
.The text from
sucrose.dat
will be saved to a file calledsugar.dat
.All of the text from
fructose.dat
,glucose.dat
,sucrose.dat
, andmaltose.txt
would be concatenated and saved to a file calledsugar.dat
.All of the text from
fructose.dat
,glucose.dat
andsucrose.dat
will be printed to the screen and saved into a file calledsugar.dat
.
Here is a video of a previous version of this workshop.