Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Shell Script Dictionary
  3. while / until

while / until

Since: POSIX(sh互換)

The while statement in shell scripts repeats a block of code as long as the specified condition is true (exit status 0). The basic form is while condition; do ... done. Using until instead loops while the condition is false (non-zero exit status). The pattern of creating an infinite loop with while true and exiting with break is widely used for interactive menus and daemon-style polling. The while read idiom is the standard way to read a file or standard input line by line.

Basic Syntax

Both while and until open their block with do and close it with done. The condition can be a [ expression ], a [[ expression ]], a command, or the built-ins true / false.

KeywordMeaningDescription
whileLoop while condition is trueRepeats the do...done block as long as the condition command exits with status 0 (true).
untilLoop while condition is falseLoops as long as the condition command exits with a non-zero status (false). It is the logical inverse of while.
doStart of the body blockMarks the beginning of the commands to repeat.
doneEnd of the body blockCloses the loop.
breakExit the loopExits the current loop immediately. Used to specify the termination condition of an infinite loop.
continueSkip to the next iterationSkips the remaining commands in the body and proceeds to the next condition evaluation.
# Basic while form
while condition; do
    commands
done

# Basic until form (inverse condition of while)
until condition; do
    commands
done

# Infinite loop (while true)
while true; do
    commands
    if exit_condition; then
        break
    fi
done

# Standard idiom to read a file line by line
while IFS= read -r line; do
    echo "${line}"
done < filename

while Loop

You can use a counter variable for counted repetition, or use the exit status of an external command to wait for a condition. To increment a counter, use ((counter++)) in bash or counter=$((counter + 1)) for POSIX sh compatibility.

FormDescription
while [ "$i" -lt 5 ]Loops while variable i is less than 5.
while [[ "$str" != "quit" ]]Loops while variable str is not equal to quit.
while commandLoops while the command exits with status 0. Useful for waiting until a ping or curl succeeds.
while trueAlways true, so this creates an infinite loop. Use break to exit explicitly.

until Loop

until loops while its condition is false, which lets you express "wait until something happens" directly. Writing while ! condition produces the same behavior, but until makes the intent clearer.

FormEquivalent while formDescription
until [ "$i" -ge 5 ]while [ "$i" -lt 5 ]Loops until variable i reaches 5 or more.
until [ -f "$file" ]while [ ! -f "$file" ]Waits until the file exists.
until ping -c1 hostwhile ! ping -c1 hostRetries until the host is reachable.

Infinite Loop (while true)

Because while true always evaluates to true, it repeats indefinitely. The standard approach is to use break inside the loop to specify the exit condition explicitly. This pattern is commonly used for interactive menus, polling loops, and long-running daemon processes.

PatternDescription
Interactive menuDisplays a menu repeatedly until the user selects the exit option.
Retry loopRepeats at a fixed interval until an operation succeeds. Combine with sleep.
PollingPeriodically checks the state of a file or process.

Combining with read (Line-by-Line Input)

The standard idiom for processing a file or standard input line by line is while IFS= read -r line; do ... done < file. Setting IFS= preserves leading and trailing spaces and tabs, and -r disables backslash escape processing.

Option / SyntaxDescription
IFS=Prevents trimming of leading and trailing whitespace (spaces and tabs). Without it, leading and trailing whitespace is stripped.
-rTreats backslashes as literal characters. Without it, backslashes may be interpreted as line-continuation escapes.
done < filePasses the file contents as standard input to the loop.
command | while readPipes a command's output into the loop. Note that a pipeline runs in a subshell, so variables modified inside the loop are not inherited by the parent shell.
while read -r col1 col2Splits each line into multiple variables. The delimiter is determined by the value of IFS (default: space and tab).

Sample Code

steinsgate_while_counter.sh
#!/bin/bash
# -----------------------------------------------
#  Demonstrates while loop (counter-controlled)
#  using Steins;Gate characters
# -----------------------------------------------

# -----------------------------------------------
#  Store lab member codenames in an array
# -----------------------------------------------

members=("Hououin Kyouma" "Assistant" "Mayuri" "Daru" "Kiryuu Moeka")
codes=(001 002 003 004 005)

echo "=== Future Gadget Laboratory - Lab Member Registration ==="
echo ""

# Loop from counter i = 0 while i is less than the array length
i=0
while [ "$i" -lt "${#members[@]}" ]; do
    # Use printf to align the number and codename
    printf "  Lab Member No.%03d  %s\n" "${codes[$i]}" "${members[$i]}"
    # Increment the counter (compatible with POSIX sh)
    i=$((i + 1))
done

echo ""
echo "Registration complete. Total lab members: ${#members[@]}"
$ chmod +x steinsgate_while_counter.sh
$ ./steinsgate_while_counter.sh
=== Future Gadget Laboratory - Lab Member Registration ===

  Lab Member No.001  Hououin Kyouma
  Lab Member No.002  Assistant
  Lab Member No.003  Mayuri
  Lab Member No.004  Daru
  Lab Member No.005  Kiryuu Moeka

Registration complete. Total lab members: 5
steinsgate_until.sh
#!/bin/bash
# -----------------------------------------------
#  Demonstrates until loop
#  using Steins;Gate characters
# -----------------------------------------------

# -----------------------------------------------
#  Count up a time-leap counter using until
# -----------------------------------------------

# Counter for Hououin Kyouma's (Okabe Rintaro's) time leaps
leap_count=0
# Keep leaping until this many time leaps have been performed
target_leap=5

echo "=== Time Leap Sequence Start ==="
echo "Goal: observe world line divergence after ${target_leap} time leaps"
echo ""

# Loop until leap_count reaches target_leap
# until loops while the condition is false:
# [ "$leap_count" -ge "$target_leap" ] is false (not yet reached) → keep looping
until [ "$leap_count" -ge "$target_leap" ]; do
    leap_count=$((leap_count + 1))
    echo "  Time leap ${leap_count}: Hououin Kyouma has leapt into the past"
done

echo ""
echo "=== ${leap_count} time leaps complete. World line divergence observed ==="
echo ""

# -----------------------------------------------
#  Demonstrate a file-wait pattern using until
# -----------------------------------------------

signal_file="/tmp/steinsgate_dmail.txt"

echo "=== Waiting for D-Mail ==="
echo ""

# Create the signal file in the background after 2 seconds
(sleep 2 && echo "El Psy Kongroo" > "${signal_file}") &

wait_count=0

# Poll every 0.5 seconds until the signal file appears
until [ -f "${signal_file}" ]; do
    wait_count=$((wait_count + 1))
    echo "  Waiting... (check #${wait_count})"
    sleep 0.5
done

echo ""
echo "  D-Mail received: $(cat "${signal_file}")"
echo "=== World line has diverged ==="

# Clean up the temporary file
rm -f "${signal_file}"
$ chmod +x steinsgate_until.sh
$ ./steinsgate_until.sh
=== Time Leap Sequence Start ===
Goal: observe world line divergence after 5 time leaps

  Time leap 1: Hououin Kyouma has leapt into the past
  Time leap 2: Hououin Kyouma has leapt into the past
  Time leap 3: Hououin Kyouma has leapt into the past
  Time leap 4: Hououin Kyouma has leapt into the past
  Time leap 5: Hououin Kyouma has leapt into the past

=== 5 time leaps complete. World line divergence observed ===

=== Waiting for D-Mail ===

  Waiting... (check #1)
  Waiting... (check #2)
  Waiting... (check #3)
  Waiting... (check #4)

  D-Mail received: El Psy Kongroo
=== World line has diverged ===
steinsgate_infinite_loop.sh
#!/bin/bash
# -----------------------------------------------
#  Demonstrates infinite loop (while true)
#  using Steins;Gate characters
# -----------------------------------------------

# -----------------------------------------------
#  Implement a lab member selection menu
#  using an infinite loop
# -----------------------------------------------

members=("Hououin Kyouma" "Assistant" "Mayuri" "Daru" "Kiryuu Moeka")

echo "=== Future Gadget Laboratory - Lab Member Lookup System ==="
echo ""

# Start an infinite loop with while true
# The menu repeats until the user enters 0
while true; do
    echo "--- Menu ---"
    # Dynamically generate menu items using a for loop
    for ((j=0; j<${#members[@]}; j++)); do
        echo "  $((j + 1)). ${members[$j]}"
    done
    echo "  0. Exit"
    echo ""
    printf "Enter a number: "
    read -r choice

    # If the user enters 0, break out of the loop
    if [ "$choice" = "0" ]; then
        echo ""
        echo "Exiting system. El Psy Kongroo."
        break
    fi

    # Check whether the input is in the range 1-5
    if [ "$choice" -ge 1 ] && [ "$choice" -le "${#members[@]}" ] 2>/dev/null; then
        index=$((choice - 1))
        echo ""
        echo "  Lab Member No.$(printf '%03d' "$choice"): ${members[$index]}"
        echo ""
    else
        # For out-of-range input, use continue to show the menu again
        echo ""
        echo "  Invalid input. Please enter a number from 1 to ${#members[@]}, or 0."
        echo ""
        continue
    fi
done
$ chmod +x steinsgate_infinite_loop.sh
$ ./steinsgate_infinite_loop.sh
=== Future Gadget Laboratory - Lab Member Lookup System ===

--- Menu ---
  1. Hououin Kyouma
  2. Assistant
  3. Mayuri
  4. Daru
  5. Kiryuu Moeka
  0. Exit

Enter a number: 3

  Lab Member No.003: Mayuri

--- Menu ---
  1. Hououin Kyouma
  2. Assistant
  3. Mayuri
  4. Daru
  5. Kiryuu Moeka
  0. Exit

Enter a number: 0

Exiting system. El Psy Kongroo.
steinsgate_while_read.sh
#!/bin/bash
# -----------------------------------------------
#  Demonstrates line-by-line reading with while read
#  using Steins;Gate characters
# -----------------------------------------------

# -----------------------------------------------
#  Create a lab member data file
# -----------------------------------------------

data_file="/tmp/steinsgate_members.tsv"

# Write lab member data as tab-separated values
# Format: lab_number[TAB]codename[TAB]real_name
cat > "${data_file}" <<'EOF'
001	Hououin Kyouma	Okabe Rintaro
002	Assistant	Makise Kurisu
003	Mayuri	Shiina Mayuri
004	Daru	Hashida Itaru
005	Kiryuu Moeka	Kiryuu Moeka
EOF

echo "=== Loading Lab Member Data ==="
echo ""

# while IFS=$'\t' read -r splits each tab-delimited line into multiple variables
# IFS=$'\t' sets the tab character as the delimiter
# -r treats backslashes as literal characters
# < "${data_file}" passes the file as standard input
while IFS=$'\t' read -r code codename realname; do
    printf "  No.%s  Codename: %-18s  Real name: %s\n" \
        "${code}" "${codename}" "${realname}"
done < "${data_file}"

echo ""
echo "=== Example: reading command output via pipe ==="
echo ""

# Pipe grep output into while read
# Because the pipeline runs in a subshell,
# variables modified inside the loop are not inherited by the parent shell
count=0
grep -v "^004" "${data_file}" | while IFS=$'\t' read -r code codename realname; do
    count=$((count + 1))
    echo "  Entry ${count}: ${codename} (${realname})"
done

echo ""
echo "Note: the count variable inside the pipe is not inherited by the parent shell"
echo "  count (parent shell) = ${count}  <- remains 0"

# Clean up the temporary file
rm -f "${data_file}"
$ chmod +x steinsgate_while_read.sh
$ ./steinsgate_while_read.sh
=== Loading Lab Member Data ===

  No.001  Codename: Hououin Kyouma      Real name: Okabe Rintaro
  No.002  Codename: Assistant           Real name: Makise Kurisu
  No.003  Codename: Mayuri              Real name: Shiina Mayuri
  No.004  Codename: Daru                Real name: Hashida Itaru
  No.005  Codename: Kiryuu Moeka        Real name: Kiryuu Moeka

=== Example: reading command output via pipe ===

  Entry 1: Hououin Kyouma (Okabe Rintaro)
  Entry 2: Assistant (Makise Kurisu)
  Entry 3: Mayuri (Shiina Mayuri)
  Entry 5: Kiryuu Moeka (Kiryuu Moeka)

Note: the count variable inside the pipe is not inherited by the parent shell
  count (parent shell) = 0  <- remains 0

Summary

In shell scripts, while loops while its condition is true and until loops while its condition is false. For counter-controlled loops, combining while [ "$i" -lt n ] with i=$((i + 1)) works in all environments including POSIX sh. An infinite loop with while true is used together with break for interactive menus and polling. To read a file line by line, the standard form is while IFS= read -r line; do ... done < file, where IFS= prevents whitespace trimming and -r treats backslashes as literal characters. When piping command output into a loop, be aware that the loop runs in a subshell, so variables modified inside the loop are not inherited by the parent shell. For conditional branching inside a loop, see if statement. For other iteration patterns, see for statement.

If you find any errors or copyright issues, please .