Bash scripting(II)

Everything Linux, A.I, IT News, DataOps, Open Source and more delivered right to you.
Subscribe
"The best Linux newsletter on the web"

This is the second article of a series focused in Gnu Bash scripting. On the first bash scripting article we’ve just created the most simple script: simple commands, one after another. We also saw some variables use.This article will cover bash control structures.

Control structures

As I’ve said on the previous article, laziness is the key to success. If I have to run some commands, a while later the same commands in the same order, when the third time comes I’ll open a text editor and paste those commands to save it as a script.

After a couple of days later, that script start to grow, both in extension and complexity. I need ways to control the execution flow, for example:

  • if something happens do something, or if something doesn’t happens do another thing;
  • while something is happening, do something over and over, or do something over and over;
  • or depending on the value of a variable or what happened, do this or that or another,
  • etc

This is what bash control structures are intended for (actually, in bash or any other programing language). But first, we need to know about:

Exit status

From the bash manpage:

«The exit status of an executed command is the value returned by the waitpid system call or equivalent function. Exit statuses fall between 0 and 255.(…) An exit status of zero indicates success (or, equals to ‘true’). A non-zero exit status indicates failure (or, equals to ‘false’).(…. For example:) If a command is not found, the child process created to execute it returns a status of 127. If a command is found but is not executable, the return status is 126».

tl;dr: exit statuts of 0 means true, success; any other value means false. It’s important to learn this because to perform true/false decisions is to check the exit status of a command.

Bash stores the exit status of the last executed command in the $? variable.

whoa, finally a screenshot!. bash scripting
$? variable in action

if-then-else

This is the if something happens (something happens means a command with an exit status of zero) do something, or else do another thing. The syntax is:

if command
then
	command1
	command2
	...
else
	commandA
	commandB
	...
fi
bash control structures. testing if-then-else commands
testing if-then-else commands

As you can see on the screenshot, it can be written on a single line, separating every command with a semi-colon:

if command; then command1; command2;...; else commandA; commandB;...;fi

Depending on the complexity of the if-then block it could be easier to read or harder to read the ‘oneliner’ style.

What if you want to run some command if something happens, run another command only if something else happens? use the keyword elif (else if):

if command
then
	command1
	command2
	...
elif other_command
	commandA
	commandB
	...
else
	another block of commands
fi

An if-then-else block finishes with the keyword fi (if backwards).

String and number comparison

Remember variables? frequently the something happens is that depending on the value of a variable we want to run some commands or other.
To compare if a string equals to some word or a number es lower than other we use the command test. The general syntax is:

test expression

If expression is omitted its considered false (exit status > 0). If expression is just a string of text it’s true. This could be tricky, for example:

$ test false; echo $?
0

We are not testing the exit status of the command false (there is a command false) but the string false against nothing. The other way to run the test command is enclosing expression with square brackets. This other way is easier to read inside an if-then-else block.

operandstrue if
expressiontrue
! expressionfalse (negates expression)
expressionA -a expressionBAnd. both expressions are true
expressionA -o expressionBOr. one of the expressions are true
string1 = string2both strings are equal
string1 != string2strings are different
int1 -eq int2integer1 is equal to integer2
int1-ge int2int1 >= int2
int1 -gt int2int1 > int2
int1 -le int2int1 <= int2
int1 -lt int2int1 < int2
int1 -ne int2int1 is not equal to int2
-e filefile exists
-d directoryfile exists and is a directory
some expressions

There are more expressions, those are the ones that I consider more important. Read the manpage for test to learn the other expressions.

bash control structures. testing the test/[ command
Some test examples. Remember exit status 0 means true, any other number false

Pay attention that there is an space between both square brackets and the expression. Now we can use test to do something based on the value of a variable. For example:

bash control structures. just an if with the [ (test) command
just an if with the [ (test) command

case

When we want to test a variable against several values we can add multiple elif to our if-then-else block like this:

if [ $a = "value1" ]
then
	command1 for value1
	...
	commandN for value1
elif [ $a = "value2" ] 
then
	command1 for value2
	...
	commandN for value2
...
elif [ $a = "valueN" ] 
then
	command1 for valueN
	...
	commandN for valueN
else
	command1 for every other value
	...
	commandN for every other value
fi

Or we can replace with a case command which is easier to read, and to write:

case expression in
	value1)
		command1 for value1
		...
		commandN for value1
		;;
	value2)
		command1 for value2
		...
		commandN for value2
		;;
	...
	*)
		command1 for every other value
		...
		commandN for every other value
		;;
esac

Also, less keystrokes, don’t forget to be lazy enough to make the computer work for you. The last value * is the default case, every other value not matched before. Case backwards, esac, is analogous to he fi statement. If the ;; operator is used, no subsequent matches are attempted after the first pattern match. To test the next pattern use the ;;* operator. For example:

case example
case example

while and until

While is do something over and over while something happens. And until is the same but negated (do something over and over while something is not happening). The general syntax is:

while command
do
	command1
	command2
	...
	commandN
done

To do something while is not happening (i.e. the exist status is different from 0), replace while with until. See some examples:

bash control structures. while and until
while and until examples

Notice they didn’t produce the same output. On the while block, when $a reach the value 4, is not more lower than 4, so the test is evaluated false (and exit code is > 0). On the until block, when $a reach the value 4, is not greater than 4.

A clock in your terminal

Sometimes I’m testing a cronjob and that makes me anxious until it gets executed. I use the true command (that, as you may figured, produces a 0 exit status) as condition, and run the command date over and over:

while true
do
	date
	sleep 1
	clear
done

This would run foverer, but I can interrupt with ctrl-c.

for-in-do

This kind of loop iterates over a list provided assigning each item to a variable. Syntax:

for i in list of words
do
	command1
	...
	commandN
done

This is easier to see in an example:

for-do example

Stay tuned

Things are getting complex, but we want our scripts to take some decisions to do this or that. I was to cover other subjects, but this article about bash control structures has grown too much.

On the next ones I’ll cover pipes and redirection. I’ll probably back to this bash control structures with the examples, something like use the output of a command in a for-in loop and stuff like that.

Everything Linux, A.I, IT News, DataOps, Open Source and more delivered right to you.
Subscribe
"The best Linux newsletter on the web"
Gonzalo Rivero
Gonzalo Rivero
I am Gonzalo, I live in Salta, a city located in the NW of Argentina. I play the guitar and a little harmonica. I also like to bike.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest articles

Join us on Facebook