MoreBash:Control flow

From Juneday education
Jump to: navigation, search

Introduction

This chapter introduces the most commonly used control flow constructs such as if, for and while.

Basic syntax examples

if and tests

The basic if-statement in Bash has the following structure:

if <command>
then
  <command>
  [<command>]*
else
  <command>
  [<command>]*
fi

The argument to if is a command (or a list of commands). The last command's exit status decides whether to enter the then-branch. The then branch can have one or more commands. The if-statement ends with fi.

Examples:

$ if ls test.sql
> then
>  echo "Found test.sql"
> fi
test.sql
Found test.sql

If we don't want the output of the test command to be shown, we can do this:

$ if ls test.sql &> /dev/null
> then
>  echo "Found test.sql"
> fi
Found test.sql

Using grep -q (quiet):

$ if grep -q Karlsson test.sql; then echo "Found Karlsson"; else echo "Didn't find Karlsson"; fi
Found Karlsson

You can write an if-statement on one line, using semicolons to separate the parts:

$ if ls test.sql &> /dev/null; then  echo "Found test.sql"; fi
Found test.sql

You can use the command [ which performs a test as if it were a boolean value. The [ command takes several arguments and the last argument must be ]. It has options to perform various comparisons and tests. For instance, -lt (less than), -gt (greater than).

If-statement with test, using [:

i=5
if [ $i -lt 10 ]
then
  echo small
else
  echo large
fi
small

Note that in the example above, [ $i -lt 10 ] is not part of the if syntax. It is an argument of type command, just like any other command. This is also why you must put spaces before and after each argument to [.

Since Bash 2.02, you should use the newer [[ keyword. It is safer and more capable. Examples:

$ myhost=$(hostname)
$ if [[ $myhost == dellasoul ]] ; then echo "I'm on dellasoul" ; else echo "I'm on some other computer"; fi
I'm on dellasoul

For arithmetic expressions and comparisons, you can use (()) in bash:

$ age=20; if ((age > 17)) ; then echo "adult"; else echo "youth"; fi
adult
$ age=17; if ((age > 17)) ; then echo "adult"; else echo "youth"; fi
youth

For-each-like for loop

Basic for-each:

for str in aa bb cc dd ee
do
  echo $str
done
aa
bb
cc
dd
ee

for num in $(seq 1 10)
do
  echo $num
done
1
2
3
4
5
6
7
8
9
10

while

Basic while-loop:

i=0
while [[ $i -lt 10 ]]
do
  echo Hello $i
  let i=i+1
done
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Hello 8
Hello 9

Bonus - Alternative syntax for for and while

Did you know that you can use C-style syntax for loops? Examples:

for ((i=0;i<10;i++)){
  echo $i;
}
0
1
2
3
4
5
6
7
8
9

for ((i=0;i<10;i++))
do
  echo $i;
done
0
1
2
3
4
5
6
7
8
9

i=0
while ((i<10))
do
  echo $i
  ((i++))
done
0
1
2
3
4
5
6
7
8
9

The traditional for loop syntax in bash is actually more of a for-each loop. It typically iterates over a list of items (typically text strings):

for str in first second third fourth fifth
do
  echo $str
done
first
second
third
fourth
fifth

Even the if-statement has a C-style-ish version:

i=10
if (( i<11 ))
then
  echo hello
fi
hello

The use of double parentheses is quite convenient if you know some programming in a real programming language. You can even use the ternary operator (for artithmetic expressions):

i=4
((val = i<5 ? 1 : 0))
echo $val
1
i=10
((val = i<5 ? 1 : 0))
echo $val
0

You can use double parentheses for the decrement -- and increment ++ operator on numeric variables as well:

i=10
echo $((i--))
10
echo $((i--))
9
echo $((i--))
8

If you don't want to use the value directly, you can leave it without the dollar sign:

i=20
((i++))
echo $i
21

Of course, you can use the prefix version of the operators to get the change before the value is used:

i=1
echo $((++i))
2

Combining the above:

i=0
while ((i++ < 10))
do
  echo $i
done 
1
2
3
4
5
6
7
8
9
10

Bonus - Select loop for a simple menu system

There is a special kind of loop for presenting a menu and reading a choice from the menu from the user.

The select loop presents the user with a list of alternatives in a menu prefixed with the menu item number. It may look like this:

$ ./select.sh 
Which is your favorite color?
1) blue
2) green
3) red
4) purple
5) quit
#? 3
you like red
#? 5

The select loop is created with a header which declares a select variable and a list of menu item texts:

select color in "blue" "green" "red" "purple" "quit"
do
  ...
done

In the do ... done block, you typically use a case to investigate what option the user chose:

select color in "blue" "green" "red" "purple" "quit"
do
    case $color in
        blue)
            echo you like blue;;
        green)
            echo you like green;;
        red)
            echo you like red;;
        purple)
            echo you like purple;;
        quit)
            break;;
        *)
            echo "Error select option 1..3";;
    esac
done

Don't forget the quit option (to get out of the loop!) and the default case, to catch wrong answers.

The complete script is shown below:

$ cat select.sh
#!/bin/bash

echo Which is your favorite color?
select color in "blue" "green" "red" "purple" "quit"
do
    case $color in
        blue)
            echo you like blue;;
        green)
            echo you like green;;
        red)
            echo you like red;;
        purple)
            echo you like purple;;
        quit)
            break;;
        *)
            echo "Error select option 1..3";;
    esac
done

An even more complete version of this script with a custom prompt and a better error message:

#!/bin/bash

echo Which is your favorite color?
PS3="Your choice: "
select color in "blue" "green" "red" "purple" "quit"
do
    case $color in
        blue)
            echo you like blue;;
        green)
            echo you like green;;
        red)
            echo you like red;;
        purple)
            echo you like purple;;
        quit)
            echo "Bye."
            break;;
        *)
            echo "$REPLY wasn't an option. Please answer with a number between 1 and 5";;
    esac
done

Sample run:

$ ./select.sh
Which is your favorite color?
1) blue
2) green
3) red
4) purple
5) quit
Your choice: 6
6 wasn't an option. Please answer with a number between 1 and 5
Your choice: 2
you like green
Your choice: 5
Bye.

Videos

Links

External links (further reading)

Chapter links

previous (Output and return) | next (Exercises Control flow)