Skip to content

scripting loops

(Written by Paul Cobbaut, https://github.com/paulcobbaut/, with contributions by: Alex M. Schapelle, https://github.com/zero-pytagoras/, Bert Van Vreckem https://github.com/bertvv/)

test

The test command can evaluate logical expressions and indicate through their exit status whether the expression was true or false. In Bash, boolean values do not exist as a data type and are represented by the exit status of commands. True is represented by the exit status 0 (denoting that the command finished successfully), and false is represented by any other exit status (1-255, denoting any failure).

Let's start by testing whether 10 is greater than 55, and then show the exit status.

[student@linux ~]$ test 10 -gt 55 ; echo $?
1

The test command returns 1 if the test fails. And as you see in the next screenshot, test returns 0 when a test succeeds.

[student@linux ~]$ test 56 -gt 55 ; echo $?
0

If you prefer true and false, then write the test like this.

[student@linux ~]$ test 56 -gt 55 && echo true || echo false
true
[student@linux ~]$ test 6 -gt 55 && echo true || echo false
false

square brackets [ ]

The test command can also be written as square brackets. In this case, the final argument must be a closing square bracket ].

The screenshot below is identical to the one above.

[student@linux ~]$ [ 56 -gt 55 ] && echo true || echo false
true
[student@linux ~]$ [ 6 -gt 55 ] && echo true || echo false
false

Remark that the square bracket is a command that takes arguments like any other command. Consequently, you must put a space between the square bracket and the arguments. You can check this fact by looking at the contents of the /bin directory.

[vagrant@el ~]$ ls /bin | head -3
[
addr2line
alias

The first command in /bin is the square bracket!

Remark that there also exists a test written as [[ ]]. This is a Bash built-in and is more powerful than the square bracket. The double square bracket is a keyword and not a command. However, this notation is specific to recent versions of Bash and is not POSIX compliant. Using it makes your script less portable. We will not discuss this notation here.

more tests

Below are some example tests. Take a look at the man page of test(1) and bash(1) (under CONDITIONAL EXPRESSIONS) to see more options for tests.

Test Description
[ -d foo ] Does the directory foo exist?
[ -e bar ] Does the file (or directory) bar exist?
[ -f foo ] Is foo a regular file?
[ -r bar ] Is bar a readable file?
[ -w bar ] Is bar a writeable file?
[ foo -nt bar ] Is file foo newer than file bar?
[ '/etc' = "${PWD}" ] Is the string /etc equal to the variable $PWD?
[ "${1}" != 'secret' ] Is the first parameter not equal to secret?
[ 55 -lt "${bar}" ] Is 55 less than the value of ${bar}?
[ "${foo}" -ge '1000' ] Is the value of ${foo} greater or equal to 1000?

Numerical values must be integers. Floating point numbers can not be interpreted by Bash.

Tests can be combined with logical AND and OR.

student@linux:~$ [ 66 -gt 55 -a 66 -lt 500 ]; echo $?
0
student@linux:~$ [ 66 -gt 55 -a 660 -lt 500 ]; echo $?
1
student@linux:~$ [ 66 -gt 55 -o 660 -lt 500 ]; echo $?
0

However, the -a and -o options are deprecated and not recommended. Instead, use the && and || operators.

student@linux:~$ [ 66 -gt 55 ] && [ 66 -lt 500 ]; echo $?
0
student@linux:~$ [ 66 -gt 55 ] && [ 660 -lt 500 ]; echo $?
1
student@linux:~$ [ 66 -gt 55 ] || [ 660 -lt 500 ]; echo $?
0

if then else

The if then else construction is about choice. If a certain condition is met, then execute something, else execute something else. The example below tests whether a file exists, and if the file exists then a proper message is echoed.

#!/bin/bash
file="isit.txt"

if [ -f "${file}" ]
then
    echo "${file} exists!"
else
    echo "${file} not found!"
fi

If we name the above script choice.sh, then it executes like this:

[student@linux scripts]$ ./choice.sh
isit.txt not found!
[student@linux scripts]$ touch isit.txt
[student@linux scripts]$ ./choice.sh
isit.txt exists!
[student@linux scripts]$

if then elif

You can nest a new if inside an else with elif. This is a simple example.

#!/bin/bash
count=42
if [ "${count}" -eq '42' ]
then
    echo "42 is correct."
elif [ "${count}" -gt '42' ]
then
    echo "Too much."
else
    echo "Not enough."
fi

for loop

The example below shows the syntax of a typical for loop in bash.

for i in 1 2 4
do
    echo "${i}"
done

An example of a for loop combined with an embedded shell.

#!/bin/bash
for counter in $(seq 1 20)
do
    echo "counting from 1 to 20, now at ${counter}"
    sleep 1
done

The same example as above can be written without the embedded shell using the Bash {from..to} shorthand.

#!/bin/bash
for counter in {1..20}
do
    echo "counting from 1 to 20, now at ${counter}"
    sleep 1
done

This for loop uses file globbing (from the shell expansion). Putting the instruction on the command line has identical functionality.

[student@linux ~]$ ls
count.sh  go.sh
[student@linux ~]$ for file in *.sh ; do cp "${file}" "${file.backup}" ; done
[student@linux ~]$ ls                                                 
count.sh  count.sh.backup  go.sh  go.sh.backup 

The for loop you know from C-like programming languages like Java can also be used in Bash. However, it is much less common.

#!/bin/bash
for (( i=0; i<10; i++ ))
do
    echo "counting from 0 to 9, now at ${i}"
done

while loop

Below a simple example of a while loop.

i=100;
while [ "${i}" -ge '0' ]
do
    echo "Counting down from 100 to 0, now at $i;"
    (( i-- ))
done

Endless loops can be made with while true or while : , where the colon : is the equivalent of no operation in the Korn and Bash shells.

#!/bin/ksh
# endless loop
while :
do
    echo "hello"
    sleep 1
done

until loop

Below a simple example of an until loop.

i=100
until [ "${i}" -le '0' ]
do
    echo "Counting down from 100 to 1, now at ${i}"
    (( i-- ))
done

practice: scripting tests and loops

  1. Write a script that uses a for loop to count from 3 to 7.

  2. Write a script that uses a for loop to count from 1 to 17000.

  3. Write a script that uses a while loop to count from 3 to 7.

  4. Write a script that uses an until loop to count down from 8 to 4.

  5. Write a script that uses a for loop to count the number of files ending in .txt in the current directory and displays a message "There are N files ending in .txt".

  6. Improve the script with conditional statements so the displayed message is also correct when there are zero files or one file ending in .txt.

solution: scripting tests and loops

  1. Write a script that uses a for loop to count from 3 to 7.

    #!/bin/bash
    
    for i in 3 4 5 6 7
    do
       echo "Counting from 3 to 7, now at ${i}"
    done
    

    You can also use brace expansion, e.g. for i in {3..7}.

  2. Write a script that uses a for loop to count from 1 to 17000.

    #!/bin/bash
    
    for i in $(seq 1 17000)
    do
        echo "Counting from 1 to 17000, now at ${i}"
    done
    
  3. Write a script that uses a while loop to count from 3 to 7.

    #!/bin/bash
    
    i=3
    while [ "${i}" -le '7' ]
    do
       echo "Counting from 3 to 7, now at ${i}"
       i=(( i+1 ))   # or (( i++ ))
    done
    
  4. Write a script that uses an until loop to count down from 8 to 4.

    #!/bin/bash
    
    i=8
    until [ "${i}" -lt '4' ]
    do
     echo "Counting down from 8 to 4, now at ${i}"
     (( i-- ))
    done
    
  5. Write a script that uses a for loop to count the number of files ending in .txt in the current directory and displays a message "There are N files ending in .txt".

    #!/bin/bash
    
    i=0
    for file in *.txt
    do
        (( i++ ))
    done
    echo "There are ${i} files ending in .txt"
    
  6. Improve the script with conditional statements so the displayed message is also correct when there are zero files or one file ending in .txt.

    #! /bin/bash
    
    if ! ls ./*.txt > /dev/null 2>&1; then
        echo "There are no files ending in .txt"
        exit 0
    fi
    
    i=0
    for file in *.txt
    do
        (( i++ ))
    done
    
    if [ "${i}" -eq '1' ]; then
        echo "There is 1 file ending in .txt"
    else
        echo "There are ${i} files ending in .txt"
    fi