Bash Scripting Concepts: Part 2 of 2
SerDigital64
Posted on December 22, 2021
Overview
This is the second part of the tutorial. If not done already, please read the first part: Bash Scripting Concepts: Part 1 of 2
Working with loops
Bash provides three ways of creating loops:
-
for
: loop for a predefined number of times -
while
: loop while the exit condition is true. The condition is evaluated before executing tasks. -
until
: loop until the exit condition is true. The condition is evaluated after executing tasks.
In addition to the loop condition, Bash provides two statements that can be used to control the loop execution flow:
-
break
: forces the loop to stop. -
continue
: forces the loop to skip remaining tasks and start the next iteration.
Command: while
In the following example, the loop iterates 5 times. The loop condition is evaluated after all commands in the code block are executed:
#!/bin/bash
declare -i count=1
declare -i max=5
while ((count <= max)); do
printf 'counter: %s\n' "${count}"
count=$((count + 1))
done
Command: until
Using the same structure as in the while
example, notice that now the loop iterates 4 times only. This is because the loop condition is evaluated before executing the code block:
#!/bin/bash
declare -i count=1
declare -i max=5
until ((count == max)); do
printf 'counter: %s\n' "${count}"
count=$((count + 1))
done
Command: for
In the case of the for
loop the iteration is predefined. Instead of having a loop condition, the loop variable count
will be assigned each value in the list:
#!/bin/bash
declare -i count
for count in 1 2 3 4 5 ; do
printf 'counter: %s\n' "${count}"
done
Working with conditionals
Bash provides the following options for implementing conditional execution:
-
||
: (logical OR) evaluates the execution of two commands and sets the exit status to zero if any associated exit status is zero. -
&&
: (logical AND) evaluates the execution of two commands and sets the exit status to zero if all associated exit statuses are zero. -
\!
: (logical NOT) evaluates the execution of a command and sets the exit status to zero if the associated exit status is not zero. -
(( ))
: performs logical evaluation on the integer expression and sets the exit status to zero if true -
[[ ]]
: evaluates the literal expression and sets the exit status to zero if true. -
if
: executes a command and if the exit status is zero then performs additional actions. -
case
: compares the provided value against a list of patterns and executes the commands upon match.
As mentioned before, Bash interprets the exit status of commands as:
-
0
: true -
>0
: false
In the following examples true
and false
are external commands that emulates true and false values (exist status 0 and 1 respectively)
Logical OR: ||
#!/bin/bash
true || false
printf 'evaluation result of (true || false): %s\n' $?
true || true
printf 'evaluation result of (true || true): %s\n' $?
false || false
printf 'evaluation result of (false || false): %s\n' $?
false || true
printf 'evaluation result of (false || true): %s\n' $?
Logical AND: &&
#!/bin/bash
true && false
printf 'evaluation result of (true && false): %s\n' $?
true && true
printf 'evaluation result of (true && true): %s\n' $?
false && false
printf 'evaluation result of (false && false): %s\n' $?
false && true
printf 'evaluation result of (false && true): %s\n' $?
Logical NOT: !
#!/bin/bash
! true
printf 'evaluation result of (! true): %s\n' $?
! false
printf 'evaluation result of (! false): %s\n' $?
Arithmetic Expression Evaluation: (( ))
The (( ))
form accepts several logical operators. Some of them are:
-
==
: equal -
!=
: not equal -
>
: greater than -
<
: less than -
>=
: greater than or equal -
<=
: less than or equal
#!/bin/bash
declare -i test=$RANDOM
(( ${test} > 5000 ))
printf 'evaluation result of (( %s > 5000 )): %s\n' ${test} $?
Expression Evaluation: [[ ]]
The [[ ]]
form accepts several logical operators and tests. Some of them are:
-
==
: equal -
!=
: not equal -
-z
: string is empty -
-n
: string is not empty -
-f
: path is a file
For ==
and !=
the special character *
can be used as a wildcard to match zero or more characters to the right.
#!/bin/bash
declare test="$RANDOM"
[[ "${test}" == 1* ]]
printf 'evaluation result of [[ "%s" == 1* ]]: %s\n' "${test}" $?
Command: if
In the following example, arithmetic evaluation is used. Notice that quotes are not required within (( ))
#!/bin/bash
declare -i test=${RANDOM}
if (( ${test} >= 10000 )); then
printf 'test value (%s) is equal or greater than 10000\n' ${test}
elif (( ${test} > 5000 || ${test} < 10000 )); then
printf 'test value (%s) is between 5001 and 9999\n' ${test}
else
printf 'test value (%s) is less than than 5001\n' ${test}
fi
Command: case
#!/bin/bash
declare -i test=${RANDOM}
case ${test} in
1*|2*) printf 'Random number (%s) starts with 1 or 2\n' ${test};;
3*) printf 'Random number (%s) starts with 3\n' ${test};;
*) printf 'Random number (%s) does not start with 1,2 or 3\n' ${test};;
esac
Redirecting data flows
Bash provides two alternatives for establishing data flows:
- Redirection
- Set read source for STDIN
- Set write destination for STDOUT
- Set write destination for STDERR
- Pipelines: integrate two commands by plugging the STDOUT from the first one to the STDIN of the second one
Redirection
In the following example, two functions will communicate with each other using a common file:
#!/bin/bash
readonly DATA_BRIDGE="$(mktemp)"
function produce_data() {
printf 'produce_data(): write data to the temporary file (%s) by redirectin the STDOUT of the printf command\n' "${DATA_BRIDGE}"
printf '[sample data]\n' >"${DATA_BRIDGE}"
}
function ingest_data() {
printf 'ingest_data(): read data from the temporary file (%s) by redirecting the STDIN of the cat command: ' "${DATA_BRIDGE}"
cat < "${DATA_BRIDGE}"
}
produce_data
ingest_data
rm -f "${DATA_BRIDGE}"
Pipelines
This example shows an alternative way of integrating both functions using pipelines:
#!/bin/bash
function produce_data() {
printf '[sample data]\n'
}
function ingest_data() {
cat
}
printf 'Integrate functions produce_data() and ingest_data() by piping their STDIN and STDOUT: '
produce_data | ingest_data
Next Steps
Discover advanced features by exploring the Bash Reference Manual:
- jobs
- signals
- traps
- parallelism
- error handling
- configuration settings
Organize your code by choosing a coding style. For example: Google Shell Style Guide
Enhance script's quality by incorporating linter and testing tools:
Copyright information
This article is licensed under a Creative Commons Attribution 4.0 International License. For copyright information on the product or products mentioned inhere refer to their respective owner.
Disclaimer
Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.
Posted on December 22, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.