mkdir d1
touch f1
mkdir: cannot create directory ‘d1’: File exists
Marie-Hélène Burle
Alex Razoumov
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.
For this section, we will play with files created by The Carpentries.
You can download them into a zip file called data.zip
with:
You can then unzip that file with:
You should now have a data
directory.
cd
into it:
Sections of a script can be executed or not depending on some conditions. To achieve this, we first need to have expressions that define these conditions.
Predicates are expressions that, when evaluated, return an exit status of 0 if they are true and an exit status of 1 if they are 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.
In its simplest form, conditional execution can be limited to the failure or success of the previous command.
xxxIn its simplest form, conditional execution can be limited to the failure or success of the previous command.
Predicates are expressions that return an exit status of 0 when they are evaluated if they are true and an exit status of 1 if they are false.
Commands can be limited to running only if the previous command ran successfully thanks to &&
.
Example:
Look at the following commands:
This is equivalent to:
and to:
This is what we did to get the data for the past few sessions.
In both cases, both commands will try to run. Now, if for some reason, the unzipping fails, we have deleted the zip file and we have to re-download it. Not a big deal here, but in some situations, executing a command if the one before fails can be a real bummer.
To prevent this, we can use the double-ampersand (&&
) operator, which plays the role of a logical AND statement:
This is equivalent to:
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.
xxxxx return 0xxxx
returns truexxx
The opposite of &&
is ||
which plays the role of a logical OR statement: the following command only gets executed if the first one fails.
Example:
Sections of scripts can be executed (or not) based on conditions thanks to if
statements.
In its simplest form, if
statements look like:
If the condition is true, the commands are executed, if the condition is false, nothing happens.
If you want a different set of commands to be executed when the condition is false, you add an else
statement:
Of course, you can have multiple conditions defining trees of if
statements. In that case, you use elif
(any number of times):
Sections of scripts can be repeated as long as a condition returns True
thanks to while loops.
The syntax of a while loop in Bash is:
The set of commands in the body of the while loop are executed as long as the predicate returns true.
Be careful that while loop can lead to infinite loops. Such loops need to be manually interrupted (by pressing <Ctrl+C>
).
Example of infinite loop:
Sections of scripts can be repeated for each element of a list thanks to for loops.
For loops run a set of commands for each item of a collection. How do you create those collections?
The least efficient method is to list all the items one by one:
Example:
file1
file2
file3
The molecules
directory contains the following .pdb
files:
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
We want to rename these files by prepending “gas_” to their current names.
Wildcards don’t work here:
The solution is to use a for loop:
This can also be written as a one-liner, although it is harder to read:
Collections can also be created with brace expansion.
Examples:
Make sure not to add a space after the commas.
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
Brace expansion can be used to create lists iterated over in loops, but also to apply commands to files or directories.
Collections can also be sequences:
Here, 1
is the start of the sequence, 10
is the end, and 2
is the step.
Such a sequence could be used in a loop this way:
The general structure of a for loop is as follows:
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?
All of the text from fructose.dat
, glucose.dat
and sucrose.dat
would be concatenated and saved to a file called sugar.dat
.
The text from sucrose.dat
will be saved to a file called sugar.dat
.
All of the text from fructose.dat
, glucose.dat
, sucrose.dat
, and maltose.txt
would be concatenated and saved to a file called sugar.dat
.
All of the text from fructose.dat
, glucose.dat
and sucrose.dat
will be printed to the screen and saved into a file called sugar.dat
.
Here is a video of a previous version of this workshop.