seq Command Explained: Generating Number Sequences
seq prints numbers. That sounds too simple to need explaining, and its options solve several small problems neatly.
The three forms
seq 5 # 1 to 5
seq 3 7 # 3 to 7
seq 0 5 100 # 0 to 100 in steps of 5
The three-argument form is start, step, end, which trips people up because the step is in the middle rather than at the end.
Padding
seq -w 1 100 | head -3
# 001
# 002
# 003
-w pads everything to the width of the largest value. This matters whenever the numbers become filenames, because file2.txt sorts after file10.txt without padding and before it with.
for i in $(seq -w 1 20); do
touch "backup-$i.tar.gz"
done
Separators
seq -s, 1 10
# 1,2,3,4,5,6,7,8,9,10
seq -s' ' 1 5
# 1 2 3 4 5
Building a comma-separated list of numbers in one command. The alternative is piping to paste with -sd,, which does the same thing.
Counting backwards
seq 10 -1 1
# 10 down to 1
Without the negative step, seq 10 1 prints nothing at all. No error, no output, and a loop that silently never runs. This is worth remembering because it fails quietly.
seq or brace expansion
Bash can generate ranges itself:
echo {1..10}
echo {1..20..2} # step
echo {01..10} # zero padded
Brace expansion is faster because no process is launched. Prefer it for fixed ranges.
The catch is that brace expansion happens before variable expansion, so this does not work:
end=10
echo {1..$end} # prints the literal {1..10}, not the numbers
That is exactly when seq is required:
for i in $(seq 1 "$end"); do echo "$i"; done
Decimals
seq 0 0.25 1
# 0
# 0.25
# 0.50
# 0.75
# 1
seq -f '%.2f' 0 0.1 0.5
Floating point rounding applies as it does everywhere, so a step of 0.1 can accumulate small errors over many iterations. -f controls the output format when the exact representation matters.
Practical uses
# Generate test files
seq -w 1 1000 | xargs -I{} touch "testfile-{}.txt"
# Ping a range of hosts
seq 1 254 | xargs -P 32 -I{} ping -c1 -W1 192.168.1.{} 2>/dev/null | grep 'bytes from'
# Repeat something N times
seq 5 | while read -r _; do curl -s -o /dev/null -w '%{time_total}\n' https://example.com; done
# Build a column of test data
seq 1 100 | paste -d, - <(seq 101 200)
That parallel ping is a quick network sweep without nmap installed, and -P 32 is what makes it finish in seconds rather than minutes.
Frequently Asked Questions
What does seq do?
It prints a sequence of numbers, one per line by default, from a start value to an end value with an optional step. It is the quickest way to generate a numeric range for a loop or for test data.
Should I use seq or a bash brace expansion in loops?
Brace expansion is faster because it is built into the shell and needs no process, so prefer it for fixed literal ranges. seq is necessary when the bounds come from variables, since brace expansion does not expand variables inside the braces.
How do I pad numbers with leading zeros?
Use -w, which pads all output to the width of the longest value. That produces 001 through 100 rather than 1 through 100, which matters when the numbers become filenames that need to sort correctly.
Can seq count backwards?
Yes, by giving a negative step. Without the step it produces nothing when the start is greater than the end, silently, which is a common source of a loop that never runs.
Does seq handle decimals?
Yes, and floating point rounding applies as it does anywhere else, so a step of 0.1 may not land exactly on the values you expect. Use -f to control the output format when precision matters.
How do I join the output onto one line?
Use -s to set the separator, or pipe to paste with its serial flag. The -s form is simpler and is how you build a comma-separated list of numbers in one command.