Bash — The continue Built-in

Learn how Bash’s `continue` statement skips the rest of the current loop iteration without stopping the loop. Explore practical examples with `for`, `while`, and `until`, filtering files and data, nested loops, and the difference between `continue` and `break`.

Bash — The continue Built-in

When working with Bash loops, you sometimes encounter an item that you don't want to process. You don't necessarily want to stop the loop—you simply want to skip the rest of the current iteration and move on.

That's what the Bash continue statement is for.

Unlike break, which exits the loop completely, continue skips the remaining commands in the current iteration and starts the next one.

A simple way to remember it:

continue → skip this iteration
break    → leave the loop

Basic Syntax

The simplest form is:

continue

Bash also supports:

continue n

where n specifies which enclosing loop should receive the continuation. This is mainly useful with nested loops and we'll look at it later.

A Simple Example

Let's start with a small loop:

for i in {1..5}
do
    if [ "$i" -eq 3 ]; then
        continue
    fi

    echo "Processing $i"
done

The output is:

Processing 1
Processing 2
Processing 4
Processing 5

When i becomes 3, Bash executes:

continue

Everything below it in that iteration is skipped, including the echo command. Bash then starts the next iteration with i=4.

Notice that the loop itself is still running.

Printing Only Even Numbers

The original example is a good way to demonstrate exactly what continue does.

Suppose we want to loop through the numbers from 1 to 10, but print only the even numbers:

#!/bin/bash

for i in {1..10}
do
    mod=$(($i % 2))

    if [ "$mod" -ne 0 ]; then
        continue
    fi

    echo "i=$i"
done

The result is:

i=2
i=4
i=6
i=8
i=10

The % operator returns the remainder of a division.

For example:

1 % 2 = 1
2 % 2 = 0
3 % 2 = 1
4 % 2 = 0

Therefore, an odd number produces a non-zero remainder.

When that happens:

if [ "$mod" -ne 0 ]; then
    continue
fi

skips the rest of the current iteration.

For an even number, the condition is false, so execution reaches:

echo "i=$i"

This gives us the desired output.

A more Bash-oriented version

Since Bash has arithmetic evaluation, the same example can be written more compactly:

#!/bin/bash

for i in {1..10}
do
    if (( i % 2 != 0 )); then
        continue
    fi

    echo "i=$i"
done

The purpose here isn't to find the shortest possible solution. The continue is deliberately visible because it demonstrates the control flow.

Filtering Items with continue

A very common pattern is to use continue as an early filter:

for item in ...
do
    if item_should_be_skipped; then
        continue
    fi

    process "$item"
done

This is particularly useful when a loop has several reasons why an item shouldn't be processed.

For example, suppose we want to process only regular files:

for file in *
do
    if [ ! -f "$file" ]; then
        continue
    fi

    echo "Processing file: $file"
done

Directories and other filesystem entries are ignored, while regular files reach the processing code.

This can be easier to read than putting the entire body inside an if block.

Skipping Empty Lines

continue is also useful when processing text.

For example:

while IFS= read -r line
do
    if [ -z "$line" ]; then
        continue
    fi

    echo "Processing: $line"
done < input.txt

Empty lines are simply ignored.

The same idea can be used to ignore comments:

while IFS= read -r line
do
    if [ -z "$line" ]; then
        continue
    fi

    if [[ "$line" == \#* ]]; then
        continue
    fi

    echo "Configuration: $line"
done < config.txt

Only lines that pass both checks reach the processing code.

Filtering Files

Another common example is processing only files with a particular extension:

for file in *
do
    if [[ "$file" != *.log ]]; then
        continue
    fi

    echo "Processing log: $file"
done

If the directory contains:

app.log
database.log
image.png
notes.txt
server.log

only the .log files are processed.

This pattern is particularly convenient when a loop performs several validation checks before doing its real work.

continue in a while Loop

continue works with while loops as well:

counter=0

while [ "$counter" -lt 10 ]
do
    counter=$((counter + 1))

    if [ "$counter" -eq 5 ]; then
        continue
    fi

    echo "counter=$counter"
done

Output:

counter=1
counter=2
counter=3
counter=4
counter=6
counter=7
counter=8
counter=9
counter=10

There is one important detail to watch for with while loops: make sure the code that advances the loop condition isn't accidentally skipped.

For example, this is dangerous:

counter=0

while [ "$counter" -lt 10 ]
do
    if [ "$counter" -eq 5 ]; then
        continue
    fi

    counter=$((counter + 1))
done

Once counter reaches 5, the continue runs before the counter is incremented. The loop then starts another iteration with the same value, and the condition remains true forever.

The safe version performs the update first:

counter=0

while [ "$counter" -lt 10 ]
do
    counter=$((counter + 1))

    if [ "$counter" -eq 5 ]; then
        continue
    fi

    echo "counter=$counter"
done

This is an important practical rule: continue must not accidentally skip whatever makes a while loop progress toward termination.

continue in an until Loop

The same mechanism works with until:

counter=0

until [ "$counter" -ge 5 ]
do
    counter=$((counter + 1))

    if [ "$counter" -eq 3 ]; then
        continue
    fi

    echo "counter=$counter"
done

Output:

counter=1
counter=2
counter=4
counter=5

Only the iteration where counter is 3 is skipped.

continue in a select Loop

Bash's select construct also supports continue.

For example:

select choice in start stop restart quit
do
    if [ "$choice" = "quit" ]; then
        break
    fi

    if [ -z "$choice" ]; then
        continue
    fi

    echo "You selected: $choice"
done

Here, an invalid or empty selection causes the menu to be displayed again, while selecting quit leaves the loop.

This is a useful illustration of the different roles of continue and break without needing another long explanation of either command.

Nested Loops

With nested loops, an ordinary continue affects the innermost loop.

For example:

for outer in {1..3}
do
    for inner in {1..3}
    do
        if [ "$inner" -eq 2 ]; then
            continue
        fi

        echo "outer=$outer inner=$inner"
    done
done

Output:

outer=1 inner=1
outer=1 inner=3
outer=2 inner=1
outer=2 inner=3
outer=3 inner=1
outer=3 inner=3

When inner is 2, only that iteration of the inner loop is skipped. The outer loop continues normally.

continue n

Bash provides an optional numeric argument when nested loops need different behavior:

continue n

For example:

for outer in {1..3}
do
    for inner in {1..3}
    do
        if [ "$inner" -eq 2 ]; then
            continue 2
        fi

        echo "outer=$outer inner=$inner"
    done

    echo "Finished outer=$outer"
done

Here:

continue 2

continues the second enclosing loop instead of merely continuing the innermost one.

The result is that when inner reaches 2, the remainder of the current outer-loop iteration is skipped as well.

Although this can be useful, continue 2 and larger values should be used sparingly. If a reader has to count several nested loops to understand where execution goes, restructuring the code may make the logic clearer.

A Practical Example

Let's put the idea to work in a more realistic script.

Suppose we receive a list of files and want to process only files that:

  1. exist,
  2. are readable, and
  3. have a .log extension.
#!/bin/bash

for file in "$@"
do
    if [ ! -e "$file" ]; then
        echo "Skipping missing file: $file"
        continue
    fi

    if [ ! -r "$file" ]; then
        echo "Skipping unreadable file: $file"
        continue
    fi

    if [[ "$file" != *.log ]]; then
        echo "Skipping non-log file: $file"
        continue
    fi

    echo "Processing log: $file"

    # Actual log processing goes here
done

Each continue removes an unsuitable item from consideration.

Once a file passes all three checks, the script reaches the actual processing code.

This structure is often cleaner than nesting the processing code inside three levels of if statements.

continue vs break

The difference is simple enough that we don't need a complicated example.

continue

means:

Skip the rest of this iteration.

While:

break

means:

Leave the loop completely.

For example, when searching for something:

for file in *
do
    if [ "$file" = "important.txt" ]; then
        echo "Found it!"
        break
    fi
done

Once the desired file is found, there is no reason to continue searching.

On the other hand, if a particular file should simply be ignored, continue is appropriate:

for file in *
do
    if [ "$file" = "temporary.txt" ]; then
        continue
    fi

    process "$file"
done

The decision is straightforward:

Skip this item?  → continue
Finished here?   → break

continuereturn, and exit

These commands operate at different levels of control flow:

CommandEffect
continueSkip the current loop iteration
breakExit the current loop
returnLeave a function
exitExit the script or shell

For example:

process_file()
{
    if [ ! -f "$1" ]; then
        return 1
    fi

    echo "Processing $1"
}

Here return is appropriate because we're leaving a function, not skipping an iteration of a loop.

Keeping these four commands conceptually separate makes Bash control flow much easier to understand.

Common Mistake: Using continue Outside a Loop

continue requires a loop context.

This doesn't make sense:

#!/bin/bash

echo "Hello"
continue

Bash will report an error because there is no loop to continue.

Likewise, if your intention is to leave a function, use return; if you need to terminate the entire script, use exit.

A Useful Design Pattern

One of the nicest ways to use continue is to put validation at the beginning of a loop:

for item in "$@"
do
    if ! valid_item "$item"; then
        continue
    fi

    process_item "$item"
done

With several checks:

for item in "$@"
do
    if [ -z "$item" ]; then
        continue
    fi

    if ! valid_item "$item"; then
        continue
    fi

    if should_ignore "$item"; then
        continue
    fi

    process_item "$item"
done

The loop effectively becomes:

check → reject → check → reject → process

The normal processing path remains easy to find, while unwanted cases leave the loop iteration early.

This is one of the main reasons continue can improve readability rather than simply adding another control-flow statement to the script.

Getting Help

Since continue is part of Bash itself, its documentation is available directly from the shell:

help continue

You can also check its type:

type continue

Bash will identify it as a shell keyword.

Quick Reference

continue

Skip the remainder of the current iteration.

continue 2

Continue the second enclosing loop.

Typical usage:

for item in ...
do
    if should_skip "$item"; then
        continue
    fi

    process "$item"
done

And the important distinction:

continue → skip this iteration
break    → exit the loop

Conclusion

continue gives Bash loops a clean way to reject individual items without stopping the entire loop.

It is especially useful when processing:

  • files
  • command-line arguments
  • configuration files
  • lines of text
  • lists of values
  • records that require validation

The basic pattern is simple:

if something_should_be_skipped; then
    continue
fi

Everything below that point is reserved for items worth processing.

In other words, continue is Bash's polite way of saying:

“Not this one. Let's move on.”

Read next

Bash — The break Built-in

Learn how Bash’s break statement lets you exit for, while, until, and select loops, including nested loops. Explore break n, practical examples, and how break differs from continue, return, and exit.

Bash — The alias Built-in

Bash aliases let you turn long, frequently used commands into short, memorable shortcuts. Learn how alias works, how to create, inspect, remove, and persist aliases, and when a function or script is a better choice.

Bash — The eval Built-in

> **Learn what Bash’s `eval` really does, why it performs a second round of shell parsing, how it enables dynamic commands and variables, and why that same power can create serious security risks. Explore safer alternatives such as arrays, functions, and indirect expansion.**