Control flow

Author

Marie-Hélène Burle

Loops and conditionals allow to change the flow of execution.

For this section, we will play with files created by The Carpentries.

You can download them into a zip file called bash.zip with:

wget http://bit.ly/bashfile -O bash.zip

You can then unzip that file with:

unzip bash.zip

You should now have a data-shell directory with a molecules subdirectory.

cd into it:

cd data-shell/molecules

For loops

To apply a set of commands to all the elements of a list, you can use a for loop.

Syntax

The general structure of a for loop is as follows:

for <iterable> in <list>
do
    <command1>
    <command2>
    ...
done

Example

The molecules directory contains the following .pdb files:

ls *.pdb
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:

mv *.pdb gas_*.pdb
mv: target 'gas_propane.pdb': Not a directory

The solution is to use a for loop:

for file in *.pdb
do
    mv $file gas_$file
done

This can also be written as a one-liner, although it is harder to read:

for file in *.pdb; do mv $file gas_$file; done

Your turn:

Using what we learnt in the string manipulation section, how could you remove the gas_ prefix to all these files?

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:

Example:

for i in file1 file2 file3
do
    echo $i
done
file1
file2
file3

Wildcards

As we have already seen, wildcards are very useful to build for loops.

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 -l {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

Brace expansion can be used to create lists iterated over in loops, but also to apply commands to files or directories.

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.

Such a sequence could be used in a loop this way:

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
  1. All of the text from fructose.dat, glucose.dat and sucrose.dat would be concatenated and saved to a file called sugar.dat.

  2. The text from sucrose.dat will be saved to a file called sugar.dat.

  3. 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.

  4. 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.

While loops

Syntax

The syntax of a while loop in Bash is:

while predicate
do
    command1
    command2
    ...
done

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:

while true
do
    echo "Press <Ctrl+C> to stop"
    sleep 1
done

Here is a video of a previous version of this workshop.

Conditionals

Syntax

if [ predicate1 ]
then
    command1
    command2
    ...
elif [ predicate2 ]
then
    command3
    command4
    ...
else
    command5
    command6
    ...
fi

Example

Let’s create a file called check.sh with the following if statement:

for f in $@
do
    if [ -e $f ]      # Make sure to have spaces around each bracket
    then
        echo $f exists
    else
        echo $f does not exist
    fi
done

Now, let’s make it executable:

chmod u+x check.sh

And let’s run this:

./check.sh file1 file2 check.sh file3

Predicates

Here are a few predicates:

[ $var == 'text' ] checks whether var is equal to 'text'.

[ $var == number ] checks whether var is equal to number.

[ -e file ] checks whether file exists.

[ -d name ] checks whether name is a directory.

[ -f name ] checks whether name is a file.