for
| Since: | for 変数 in リスト | POSIX(sh互換) |
|---|---|---|
| C言語風 for (( )) | Bash(bash拡張) |
The for statement in shell scripts repeats the same process for each element in a list or sequence. The basic form is for variable in list, which assigns one element at a time to the variable. In bash, you can also use the C-style for ((i=0; i<n; i++)) form. Combined with command substitution ($(command)), you can use command output directly as a list, and combined with file globs, you can process all files in a directory at once.
Basic Syntax
A for loop starts its block with do and closes it with done. Each value listed after in — whether a literal value, variable, or command substitution result — is assigned to the loop variable one at a time.
| Keyword | Meaning | Description |
|---|---|---|
for | Start of the loop | Declares the loop variable and the list to iterate over. |
in | List specification | Lists the values to iterate over. If omitted, $@ (all arguments) is used. |
do | Start of the body block | Marks the beginning of the statements to repeat. |
done | End of the body block | Closes the loop (it is "do" spelled backwards). |
break | Exit the loop | Immediately exits the current for loop. |
continue | Skip to next iteration | Skips the remaining statements and moves on to the next element. |
# Basic for...in form
for variable in value1 value2 value3; do
# variable is assigned value1, then value2, then value3 in order
process
done
# C-style numeric loop (bash only)
for ((i=0; i<n; i++)); do
process
done
# Brace expansion to generate a sequence (bash only)
for i in {1..5}; do
process
done
# Command substitution: use command output as the list
for variable in $(command); do
process
done
# Glob: iterate over files
for file in /path/to/*.txt; do
process
done
for...in List Form
You can list values separated by spaces after in, or expand an array. To expand an entire array, write "${array[@]}". Without double quotes, elements containing spaces will be split by word splitting.
| Syntax | Description |
|---|---|
for x in a b c | Lists values directly. The list is space-separated. |
for x in "${arr[@]}" | Safely expands all array elements. Elements containing spaces are treated as a single item. |
for x in "${arr[*]}" | Expands the array as a single string joined by the IFS separator. Use [@] in most cases. |
for x in {1..10} | Expands to an integer sequence from 1 to 10 (bash only). |
for x in {0..9..2} | Expands from 0 to 9 in steps of 2 (bash only, with step). |
C-Style for Form (bash only)
The for ((init; condition; update)) form is a bash-only arithmetic for loop. It is useful when you need to control an index or counter. Because it is not available in sh, use a while loop instead when portability is required.
| Part | Description |
|---|---|
| init | Executed once before the loop starts. Sets the initial value of the counter variable. |
| condition | Evaluated before each iteration. The loop continues while the result is true (non-zero). |
| update | Executed after each iteration. Typically increments or decrements the counter. |
# Basic C-style form
for ((i=0; i<5; i++)); do
echo "i = ${i}"
done
# Decrement (countdown)
for ((i=5; i>0; i--)); do
echo "Countdown: ${i}"
done
# Increment by 2 each step
for ((i=0; i<=10; i+=2)); do
echo "Even: ${i}"
done
Combining with Command Substitution
You can use the output of $(command) as the list for a for loop. This lets you iterate directly over the output of commands like seq, ls, find, or cat, enabling flexible processing combined with external commands. However, if filenames contain spaces or newlines, glob expansion is safer.
| Common combination | Description |
|---|---|
for i in $(seq 1 10) | Generates a numeric list from 1 to 10 using the seq command. Works in sh as well. |
for line in $(cat file.txt) | Iterates over each line of a file. Note that lines containing spaces will be split. |
for f in $(find . -name "*.log") | Iterates over find results. Cannot handle paths that contain spaces. |
for host in $(cat hosts.txt) | Iterates over each line in a host list file. Useful for bulk SSH operations. |
Iterating Over a File List
To process files in a directory, glob expansion (e.g., *.txt) is safer than command substitution. Because the shell handles glob expansion, it correctly handles filenames that contain spaces. You can also use the nullglob option (bash) to handle the case where no files match.
| Syntax | Description |
|---|---|
for f in /dir/*.txt | Iterates over .txt files in the specified directory. |
for f in ./* | Iterates over all files and directories in the current directory. |
for f in /dir/**/*.log | Recursively iterates including subdirectories (requires bash's globstar option). |
shopt -s nullglob | When no files match the glob, prevents the pattern from being treated as a literal string. |
Sample Code
yakuza_for_in.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates for...in list form
# using characters from the Yakuza series
# -----------------------------------------------
# -----------------------------------------------
# Store character names in an array
# -----------------------------------------------
characters=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
echo "=== Yakuza Character List ==="
echo ""
# Iterate over all array elements with for...in
# Always enclose "${array[@]}" in double quotes
# so that names containing spaces are treated as single elements
for chara in "${characters[@]}"; do
echo " - ${chara}"
done
echo ""
echo "=== Display with Numbers ==="
echo ""
# Use C-style for to display with an index
for ((i=0; i<${#characters[@]}; i++)); do
# ${#array[@]} returns the number of elements in the array
echo " ${i}: ${characters[$i]}"
done
echo ""
echo "Character count: ${#characters[@]}"
$ chmod +x yakuza_for_in.sh $ ./yakuza_for_in.sh === Yakuza Character List === - 桐生一馬 - 真島吾朗 - 秋山駿 - 冴島大河 - 錦山彰 === Display with Numbers === 0: 桐生一馬 1: 真島吾朗 2: 秋山駿 3: 冴島大河 4: 錦山彰 Character count: 5
yakuza_c_style_for.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates C-style for loop
# using characters from the Yakuza series
# -----------------------------------------------
# -----------------------------------------------
# Store character combat scores in an array
# -----------------------------------------------
names=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
scores=(9800 9500 8200 9100 8700)
echo "=== Combat Score Ranking (C-style for) ==="
echo ""
# Control the index with C-style for
# Increment i from 0 up to (but not including) the array length
for ((i=0; i<${#names[@]}; i++)); do
name="${names[$i]}"
score="${scores[$i]}"
printf " #%d %-16s Score: %d\n" "$((i + 1))" "${name}" "${score}"
done
echo ""
echo "=== Countdown (Decrement) ==="
echo ""
# Decrement i from the array length down to 1
for ((i=${#names[@]}; i>=1; i--)); do
echo " Rank ${i}: ${names[$((i - 1))]}"
done
echo ""
echo "=== Show Only Even-Index Characters ==="
echo ""
# Step by 2 to pick only even-index elements
for ((i=0; i<${#names[@]}; i+=2)); do
echo " ${names[$i]} (index ${i})"
done
$ chmod +x yakuza_c_style_for.sh $ ./yakuza_c_style_for.sh === Combat Score Ranking (C-style for) === #1 桐生一馬 Score: 9800 #2 真島吾朗 Score: 9500 #3 冴島大河 Score: 9100 #4 錦山彰 Score: 8700 #5 秋山駿 Score: 8200 === Countdown (Decrement) === Rank 5: 錦山彰 Rank 4: 冴島大河 Rank 3: 秋山駿 Rank 2: 真島吾朗 Rank 1: 桐生一馬 === Show Only Even-Index Characters === 桐生一馬 (index 0) 秋山駿 (index 2) 錦山彰 (index 4)
yakuza_cmd_sub_for.sh
#!/bin/bash
# -----------------------------------------------
# Combines command substitution with a for loop
# to process character data files
# -----------------------------------------------
# -----------------------------------------------
# Prepare working directory and data files
# -----------------------------------------------
work_dir="./yakuza_data"
mkdir -p "${work_dir}"
# Create an information file for each character
echo "桐生一馬 神室町 極道" > "${work_dir}/kiryu.txt"
echo "真島吾朗 蒼天堀 狂犬" > "${work_dir}/majima.txt"
echo "秋山駿 横浜 探偵" > "${work_dir}/akiyama.txt"
echo "冴島大河 北海道 熊殺し" > "${work_dir}/saejima.txt"
echo "錦山彰 神室町 龍" > "${work_dir}/nishikiyama.txt"
echo "=== Iteration with seq command ==="
echo ""
# Use seq to generate numbers 1–5 and iterate over them
# The $() command substitution uses seq's output as the list
for i in $(seq 1 5); do
echo " Round ${i} start"
done
echo ""
echo "=== Iterate over find results to process files ==="
echo ""
# Search for text files with find and receive the results via command substitution
# Safe to use as long as filenames do not contain spaces
for filepath in $(find "${work_dir}" -name "*.txt" | sort); do
filename=$(basename "${filepath}" .txt)
content=$(cat "${filepath}")
echo " [${filename}] ${content}"
done
echo ""
echo "=== Safely iterate over files using glob expansion ==="
echo ""
# Glob expansion handles filenames with spaces correctly
# Prefer glob over command substitution for file iteration
for f in "${work_dir}"/*.txt; do
# Check that the file exists before processing
if [ -f "${f}" ]; then
echo " Processing: $(basename ${f})"
fi
done
# Remove the working directory to clean up
rm -rf "${work_dir}"
echo ""
echo "Cleanup complete."
$ chmod +x yakuza_cmd_sub_for.sh $ ./yakuza_cmd_sub_for.sh === Iteration with seq command === Round 1 start Round 2 start Round 3 start Round 4 start Round 5 start === Iterate over find results to process files === [akiyama] 秋山駿 横浜 探偵 [kiryu] 桐生一馬 神室町 極道 [majima] 真島吾朗 蒼天堀 狂犬 [nishikiyama] 錦山彰 神室町 龍 [saejima] 冴島大河 北海道 熊殺し === Safely iterate over files using glob expansion === Processing: akiyama.txt Processing: kiryu.txt Processing: majima.txt Processing: nishikiyama.txt Processing: saejima.txt Cleanup complete.
yakuza_break_continue.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates the use of break and continue
# -----------------------------------------------
names=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
# Status flag for each character (1: available, 0: resting)
status=(1 1 0 1 0)
echo "=== continue: Skip characters who are resting ==="
echo ""
for ((i=0; i<${#names[@]}; i++)); do
# Use continue to skip when status is 0 (resting)
if [ "${status[$i]}" -eq 0 ]; then
echo " ${names[$i]}: Resting — skipped"
continue
fi
echo " ${names[$i]}: Ready"
done
echo ""
echo "=== break: Stop searching once Kiryu is found ==="
echo ""
target="桐生一馬"
found=0
for chara in "${names[@]}"; do
echo " Searching: ${chara}"
# Use break to exit the loop when the target is found
if [ "${chara}" = "${target}" ]; then
echo " → Found ${target}! Stopping search."
found=1
break
fi
done
echo ""
if [ "${found}" -eq 1 ]; then
echo "Found: ${target}"
else
echo "${target} was not found."
fi
$ chmod +x yakuza_break_continue.sh $ ./yakuza_break_continue.sh === continue: Skip characters who are resting === 桐生一馬: Ready 真島吾朗: Ready 秋山駿: Resting — skipped 冴島大河: Ready 錦山彰: Resting — skipped === break: Stop searching once Kiryu is found === Searching: 桐生一馬 → Found 桐生一馬! Stopping search. Found: 桐生一馬
Summary
Shell script for loops come in two forms: for variable in list, and the bash-only for ((init; condition; update)). When iterating over all elements of an array, enclose the expansion in double quotes as "${array[@]}" to prevent elements containing spaces from being split. Using command substitution ($()), you can iterate directly over the output of commands like seq or find, but this cannot handle filenames with spaces. For file iteration, glob expansion (e.g., *.txt) is the safer choice. Use break to exit a loop immediately, and continue to skip the current iteration and move to the next. When combining loops with conditionals, also refer to the if statement page.
If you find any errors or copyright issues, please contact us.