*Thursday, 25th June 2026 - 11:22 UTC+01:00*
# IF Statements
An important part of controlling a Bash script is to conditionally execute code **IF** a condition is met.<br>
A basic IF statement in Bash has the following structure:
```bash
if [ CONDITION ]; then
# Some code
else
# Some other code
fi
```
Things to note:
- Spaces between square brackets and conditional elements inside the first line.
- Semi-colon is required after the close-bracket.
One example of the uses for an IF statement is a basic string comparison, such as the example below:
```bash
x="Queen"
if [ $x == "King" ]; then
echo "$x is a King!"
else
echo "$x is not a King!"
fi
```
```
Queen is not a King!
```
> [!note]
> An exclamation mark and an equals sign - `!=` - can be used for "not equal to."
## Arithmetic IF Statements
Arithmetic IF statements can use the double-parentheses structure:
```bash
x=10
if (($x > 5)); then
echo "$x is more than 5!"
fi
```
```
10 is more than 5!
```
Arithmetic IF statements can also use square brackets and an arithmetic flag rather than (`>`, `<`, `=`, `!=`, etc.):
- `-eq` - equal to
- `-ne` - not equal to
- `-lt` - less than
- `-le` - less than or equal to
- `-gt` - greater than
- `-ge` - greater than or equal to
Example:
```bash
x=10
if [ $x -gt 5 ]; then
echo "$x in more than 5!"
fi
```
```
10 is more than 5!
```
## Other Bash Conditional Flags
Bash also have a variety of file-related flags, such as:
- `-e` - if the file exists
- `-s` - if the file exists and filesize is greater than zero
- `-r` - if the file exists and is readable
- `-w` - if the file exists and is writable
[[https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html | GNU's Bash Conditional Expressions list]] shows the full list.
## Using `AND` and `OR` in Bash
To combine conditions (`AND`) or use an `OR` statement in Bash, the following symbols can be used:
- `&&` for AND
- `||` for OR
In Bash, conditionals can be chained as follows:
```bash
x=10
if [ $x -gt 5 ] && [ $x -lt 11 ]; then
echo "$x is more than 5 and less than 11!"
fi
```
Or double-square-bracket notation can be used:
```bash
x=10
if [[ $x -gt 5 && $x -lt 11 ]]; then
echo "$x is more than 5 and less than 11!"
fi
```
## `IF` and CLI programs
Many CLI programs can be used directly in the conditional, removing the square brackets. For example, if the file `Hello_There.txt` has "General Kenobi" inside:
```bash
if grep -q "General Kenobi" Hello_There.txt; then
echo "You are a bold one!"
fi
```
```
You are a bold one!
```
> [!note]
> The `-q` flag is for "quiet" so it does not return the matched lines like grep normally does.
## `IF` with shell-within-a-shell
A shell-within-a-shell can also be used for the conditional.
```bash
if $(grep -q "General Kenobi" Hello_There.txt); then
echo "You are a bold one!"
fi
```
```
You are a bold one!
```
Here is an example of how these `IF` statements and conditions work when sorting model results:
```bash
# Extract Accuracy from first ARGV element
accuracy=$(grep Accuracy $1 | sed 's/.* //')
# Conditionally move into good_models folder
if [ $accuracy -ge 90 ]; then
mv $1 good_models/
fi
# Conditionally move into bad_models folder
if [ $accuracy -lt 90 ]; then
mv $1 bad_models/
fi
```
Another example. This one creates a variable `sfile` out of the first ARGV element, uses an `IF` statement and `grep` to check if the `sfile` variable contians `SRVM_` and `vpt` inside, and moves the matching files to `good_logs/`:
```bash
# Create variable from first ARGV element
sfile=$1
# Create an IF statement on sfile's contents
if grep -q 'SRVM_' $sfile && grep -q 'vpt' $sfile; then
# Move file if matched
mv $sfile good_logs/
fi
```
## `FOR` loops and `WHILE` statements
### `FOR` Loops
In Python, a FOR loop would be structured thusly:
```python
for x in range(3):
print(x)
```
```
0
1
2
```
And in R, a FOR loop would be structured like this:
```r
for (x in seq(3)){
print(x)
}
```
```
[1] 1
[1] 2
[1] 3
```
The basic structure in Bash is similar:
```bash
for x in 1 2 3
do
echo $x
done
```
```
1
2
3
```
Bash can create a numeric range through something called "brace expansion". It takes the format `{START..STOP..INCREMENT}`.
```bash
# This brace expansion starts at one, finishes at five, increments by two.
for x in {1..5..2}
do
echo $x
done
```
```
1
3
5
```
For numerical loops, Bash also has "three expression syntax", which is another common way of writing FOR loops.
- Surround three expressions with double parentheses.
- The first part is the start expression (`x=2`)
- The middle part is the terminating condition (`x<=4`)
- The end part is the increment (or decrement) expression (`x+=2`)
```bash
for ((x=2;x<=4,x+=2))
do
echo x
done
```
```
2
4
```
Bash also allows pattern-matching expressiosn (glob expansion) into a for loop using the `*` symbol, such as files in a directory.<br>
For example, if there are two text documents in the folder `/books`:
```bash
for book in books/*
do
echo $book
done
```
```
books/book1.txt
books/book2.txt
```
This will automatically create an array to iterate over and print out the file name.
A shell-within-a-shell can also be used in for loops:
```bash
for book in $(ls books/ | grep -i 'air')
do
echo $book
done
```
```
AirportBook.txt
FairMarketBook.txt
```
### `WHILE` Loops
A `WHILE` loop is similar to a `FOR` loop, except that you set a condition that is tested at each iteration, and continues until the condition is no longer met.
- Use the word `while` instead of `for`
- Surround the condition in square brackets
- Use of same flags for numerical comparison from IF statements (such as `-le`)
- Multiple conditions can be chained or use double-brackers just like IF statements along with `&&` (AND) or `||` (OR).
- Ensure that there is a change inside the code that will trigger a stop, otherwise you may have an infinite loop.
Example:
```bash
x=1
while [ $x -le 3];
do
echo $x
((x+=1))
done
```
```
1
2
3
```
Beware of infinite loops, which happens if the break condition is never met:
```bash
x=1
while [ $x -le 3 ];
do
echo $x
# Don't increment x. It never reaches 3.
# ((x+=1))
done
```
```
1
1
1
...
```
...ad infinitum.
---
Let's say you wanted to output all the file names with a specified file extension that also live in a specified folder. You would have a script that looks a little bit like this:
```bash
# Use a for look on files in directory
for file in inherited_folder/*.R
do
# Echo out each file
echo $file
done
```
Or if you had a dump of files and you wanted to extract `.py` files with a specific string in its name to the folder `to_keep/`:
```bash
# Create a FOR statement on files in directory
for file in old_files/*.py
do
# Create IF statement using grep
if grep -e 'file_name_string' $file ; then
# Move wanted files to to_keep/ folder
mv $file to_keep/
fi
done
```
## `CASE` Statements
Case statements can be preferable over IF statements when there are multiple or complex conditionals in play. For example, if the following conditions and actions were to be tested:
- If a file contains `sydney` then move it into the `/sydney` directory
- If a file contains `melbourne` or `brisbane` then delete it
- If a file contains `canberra` then rename it to `IMPORTANT_filename` where `filename` was the original filename.
One way of doing all this is with a complex IF statement that:
- Calls `grep` on the first ARGV argument for the conditional.
```bash
if grep -q 'sydney' $1; then
mv $1 sydney/
fi
if grep -q 'melbourne|brisbane' $1; then
rm $1
fi
if grep -q 'canberra' $1; then
mv $1 "IMPORTANT_$1"
fi
```
Given the repetitiveness of the IF statement, a CASE statement would be preferable. With a CASE statement, you could begin by selecting which variable or string is to be matched against (and even call shell-within-a-shell if needed), and you could add as many possible matches and actions as you like.
A CASE statement is structured like:
```bash
case 'STRINGVAR' in
PATTERN1)
COMMAND1;;
PATTERN2)
COMMAND2;;
*)
DEFAULT COMMAND;;
esac
```
Regex can be used for the `PATTERN`, such as `Air*` or `*hat*`. <br>
Make sure to separate the patteern and code to run by a close-parentheses and finish commands with a double semi-colon.
It is common, though not required, to finish with a default command - `*) DEFAULT COMMAND;;` that runs if none of the other patterns match.<br>
The finishing word is `esac`.
The old IF statement, rewritten as a CASE statement, becomes:
```bash
case $(cat $1) in
*sydney*)
mv $1 sydney/ ;;
*melbourne*|*brisbane*)
rm $1 ;;
*canberra*)
mv $1 "IMPORTANT_$1" ;;
*)
echo "No cities found" ;;
esac
```
Another example:
```bash
# Create a CASE statement matching the first ARGV element
case $1 in
# Match on all weekdays
Monday|Tuesday|Wednesday|Thursday|Friday)
echo "It is a Weekday!" ;;
# Match on all weekend days
Saturday|Sunday)
echo "It is a Weekend!" ;;
# Create a default
*)
echo "Not a day!" ;;
esac
```
Another example:
```bash
# Use a FOR loop for each file in 'model_out'
for file in model_out/*
do
# Create a CASE statement for each file's contents
case $(cat $file) in
# Match on tree and non-tree models
*"Random Forest"*|*GBM*|*XGBoost*)
mv $file tree_models/ ;;
*KNN*|*Logistic*)
rm $file ;;
# Create a default
*)
echo "Unknown model in $file" ;;
esac
done
```