Control Structures
If statement
Section titled “If statement”if [[ $1 -eq 1 ]]; then echo "1 was passed in the first parameter"elif [[ $1 -gt 2 ]]; then echo "2 was not passed in the first parameter"else echo "The first parameter was not 1 and is not more than 2."fiThe closing fi is necessary, but the elif and/or the else clauses can be omitted.
The semicolons before then are standard syntax for combining two commands on a single line; they can be omitted only if then is moved to the next line.
It’s important to understand that the brackets [[ are not part of the syntax, but are treated as a command; it is the exit code from this command that is being tested. Therefore, you must always include spaces around the brackets.
This also means that the result of any command can be tested. If the exit code from the command is a zero, the statement is considered true.
if grep "foo" bar.txt; then echo "foo was found"else echo "foo was not found"fiMathematical expressions, when placed inside double parentheses, also return 0 or 1 in the same way, and can also be tested:
if (( $1 + 5 > 91 )); then echo "$1 is greater than 86"fiYou may also come across if statements with single brackets. These are defined in the POSIX standard and are guaranteed to work in all POSIX-compliant shells including Bash. The syntax is very similar to that in Bash:
if [ "$1" -eq 1 ]; then echo "1 was passed in the first parameter"elif [ "$1" -gt 2 ]; then echo "2 was not passed in the first parameter"else echo "The first parameter was not 1 and is not more than 2."fiLooping over an array
Section titled “Looping over an array”for loop:
arr=(a b c d e f)for i in "${arr[@]}";do echo "$i"doneOr
for ((i=0;i<${#arr[@]};i++));do echo "${arr[$i]}"donewhile loop:
i=0while [ $i -lt ${#arr[@]} ];do echo "${arr[$i]}" i=$(expr $i + 1)doneOr
i=0while (( $i < ${#arr[@]} ));do echo "${arr[$i]}" ((i++))doneUsing For Loop to List Iterate Over Numbers
Section titled “Using For Loop to List Iterate Over Numbers”#! /bin/bash
for i in {1..10}; do # {1..10} expands to "1 2 3 4 5 6 7 8 9 10" echo $idoneThis outputs the following:
12345678810Conditional execution of command lists
Section titled “Conditional execution of command lists”How to use conditional execution of command lists
Section titled “How to use conditional execution of command lists”Any builtin command, expression, or function, as well as any external command or script can be executed conditionally using the &&(and) and ||(or) operators.
For example, this will only print the current directory if the cd command was successful.
cd my_directory && pwdLikewise, this will exit if the cd command fails, preventing catastrophe:
cd my_directory || exitrm -rf *When combining multiple statements in this manner, it’s important to remember that (unlike many C-style languages) these operators have no precedence and are left-associative.
Thus, this statement will work as expected…
cd my_directory && pwd || echo "No such directory"- If the
cdsucceeds, the&& pwdexecutes and the current working directory name is printed. Unlesspwdfails (a rarity) the|| echo ...will not be executed. - If the
cdfails, the&& pwdwill be skipped and the|| echo ...will run.
But this will not (if you’re thinking if...then...else)…
cd my_directory && ls || echo "No such directory"- If the
cdfails, the&& lsis skipped and the|| echo ...is executed. - If the `cd` succeeds, the `&& ls` is executed.
- If the
lssucceeds, the|| echo ...is ignored. (so far so good) - ****BUT... if the `ls` fails, the `|| echo ...` will also be executed.****
****It is the `ls`****, not the `cd`, ****that is the previous command****.
Why use conditional execution of command lists
Section titled “Why use conditional execution of command lists”Conditional execution is a hair faster than
if...thenbut its main advantage is allowing functions and scripts to exit early, or “short circuit”.Unlike many languages like
Cwhere memory is explicitly allocated for structs and variables and such (and thus must be deallocated),bashhandles this under the covers. In most cases, we don’t have to clean up anything before leaving the function. Areturnstatement will deallocate everything local to the function and pickup execution at the return address on the stack.Returning from functions or exiting scripts as soon as possible can thus significantly improve performance and reduce system load by avoiding the unnecessary execution of code. For example…
Terminal window my_function () {### ALWAYS CHECK THE RETURN CODE# one argument required. "" evaluates to false(1)[[ "$1" ]] || return 1# work with the argument. exit on failuredo_something_with "$1" || return 1do_something_else || return 1# Success! no failures detected, or we wouldn't be herereturn 0}While Loop
Section titled “While Loop”#! /bin/bashi=0while [ $i -lt 5 ] #While i is less than 5doecho "i is currently $i"i=$[$i+1] #Not the lack of spaces around the brackets. This makes it a not a test expressiondone #ends the loopWatch that there are spaces around the brackets during the test (after the while statement). These spaces are necessary.
This loop outputs:
Terminal window i is currently 0i is currently 1i is currently 2i is currently 3i is currently 4For Loop with C-style syntax
Section titled “For Loop with C-style syntax”The basic format of C-style
forloop is:Terminal window for (( variable assignment; condition; iteration process ))Notes:
- The assignment of the variable inside C-style
forloop can contain spaces unlike the usual assignment - Variables inside C-style
forloop aren’t preceded with$.
Example:
Terminal window for (( i = 0; i < 10; i++ ))doecho "The iteration number is $i"doneAlso we can process multiple variables inside C-style
forloop:Terminal window for (( i = 0, j = 0; i < 10; i++, j = i * i ))doecho "The square of $i is equal to $j"doneUntil Loop
Section titled “Until Loop”Until loop executes until condition is true
Terminal window i=5until [[ i -eq 10 ]]; do #Checks if i=10echo "i=$i" #Print the value of ii=$((i+1)) #Increment i by 1doneOutput:
Terminal window i=5i=6i=7i=8i=9When
ireaches 10 the condition in until loop becomes true and the loop ends.continue and break
Section titled “continue and break”Example for continue
Terminal window for i in [series]docommand 1command 2if (condition) # Condition to jump over command 3continue # skip to the next value in "series"ficommand 3doneExample for break
Terminal window for i in [series]docommand 4if (condition) # Condition to break the loopthencommand 5 # Command if the loop needs to be brokenbreakficommand 6 # Command to run if the "condition" is never truedoneLoop break
Section titled “Loop break”Break multiple loop:
Terminal window arr=(a b c d e f)for i in "${arr[@]}";doecho "$i"for j in "${arr[@]}";doecho "$j"break 2donedoneOutput:
Terminal window aaBreak single loop:
Terminal window arr=(a b c d e f)for i in "${arr[@]}";doecho "$i"for j in "${arr[@]}";doecho "$j"breakdonedoneOutput:
Terminal window aabacadaeafaSwitch statement with case
Section titled “Switch statement with case”With the
casestatement you can match values against one variable.The argument passed to
caseis expanded and try to match against each patterns.If a match is found, the commands upto
;;are executed.Terminal window case "$BASH_VERSION" in[34]*)echo {1..4};;*)seq -s" " 1 4esacPattern are not regular expressions but shell pattern matching (aka globs).
For Loop without a list-of-words parameter
Section titled “For Loop without a list-of-words parameter”Terminal window for arg; doecho arg=$argdoneA
forloop without a list of words parameter will iterate over the positional parameters instead. In other words, the above example is equivalent to this code:Terminal window for arg in "$@"; doecho arg=$argdoneIn other words, if you catch yourself writing
for i in "$@"; do ...; done, just drop theinpart, and write simplyfor i; do ...; done.For Loop
Section titled “For Loop”#! /bin/bashfor i in 1 "test" 3; do #Each space separated statement is assigned to iecho $idoneOther commands can generate statements to loop over. See “Using For Loop to Iterate Over Numbers” example.
This outputs:
Terminal window 1test3Syntax
Section titled “Syntax”- [ “$1” = “$2” ] #A ”[” bracket is actually a command. Because of this it requires a space befor and after it.
- test “$1” = “$2” #Test is a synonym for the ”[” command
Parameters
Section titled “Parameters”Parameter to [ or test Details File Operators Details -e "$file"Returns true if the file exists. -d "$file"Returns true if the file exists and is a directory -f "$file"Returns true if the file exists and is a regular file -h "$file"Returns true if the file exists and is a symbolic link String Comparators Details -z "$str"True if length of string is zero -n "$strTrue if length of string is non-zero "$str" = "$str2"True if string $str is equal to string $str2. Not best for integers. It may work but will be inconsitent "$str" != "$str2"True if the strings are not equal Integer Comparators Details "$int1" -eq "$int2"True if the integers are equal "$int1" -ne "$int2"True if the integers are not equals "$int1" -gt "$int2"True if int1 is greater than int 2 "$int1" -ge "$int2"True if int1 is greater than or equal to int2 "$int1" -lt "$int2"True if int1 is less than int 2 "$int1" -le "$int2"True if int1 is less than or equal to int2 Remarks
Section titled “Remarks”There are many comparator parameters available in bash. Not all are yet listed here.
- If the