*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 ```