646

I need to loop some values,

for i in $(seq $first $last) do does something here done 

For $first and $last, I need it to be of fixed length 5. So if the input is 1, I need to add zeros in front such that it becomes 00001. It loops till 99999 for example, but the length has to be 5.

E.g.: 00002, 00042, 00212, 12312 and so forth.

Any idea on how I can do that?

5
  • 61
    A good answer could be: seq - w 1 99999. Option -w keeps output lenght constant, padding shortest numbers with 0. Commented Jan 27, 2014 at 13:04
  • 1
    Also rename files: stackoverflow.com/questions/55754/bash-script-to-pad-file-names Commented Oct 1, 2015 at 7:43
  • 1
    Many answers here recommend for variable in $(something to generate the numbers); do ... but this is problematic when the list of numbers is long. It is much more efficient to use something to generate the numbers | while read -r variable; do .... See also mywiki.wooledge.org/DontReadLinesWithFor which discusses reading lines from files etc, but some of the arguments apply here too. Commented Nov 23, 2018 at 5:40
  • 1
    See how to zero-padding with parameter expansion: askubuntu.com/a/1257316/670392 Commented Jul 8, 2020 at 10:36
  • If you need out of sequence sequences, seq -w will pad to the zero-padded numbers you pass e.g. $(seq -w 9 32) $(seq -w 01 8). Subtle. But useful to know. Commented Aug 7, 2024 at 14:18

18 Answers 18

1044

In your specific case though it's probably easiest to use the -f flag to seq to get it to format the numbers as it outputs the list. For example:

for i in $(seq -f "%05g" 10 15) do echo $i done 

will produce the following output:

00010 00011 00012 00013 00014 00015 

More generally, bash has printf as a built-in so you can pad output with zeroes as follows:

$ i=99 $ printf "%05d\n" $i 00099 

You can use the -v flag to store the output in another variable:

$ i=99 $ printf -v j "%05d" $i $ echo $j 00099 

Notice that printf supports a slightly different format to seq so you need to use %05d instead of %05g.

Sign up to request clarification or add additional context in comments.

5 Comments

%05g instead of %05d fixed it for me. "00026" was giving me "00022". Thanks!
Great, comprehensive answer. One small correction: strictly speaking, seq supports a subset of the format chars. that printf supports (and that subsets includes g, but not d). The behavior of characters d and g differs subtly in that d interprets 0-prefixed numbers strings as octal numbers and converts them to decimal, whereas g treats them as decimals. (@EdManet: that's why '00026' turned into '00022' with d). Another thing worth mentioning: seq -w does automatic zero-padding of the output numbers based on the widest number generated.
This helped me generate hex characters list. for (( i = 0; i <= 0xffffffff; i++ )) do printf "%08x\n" $i ; done >> hex.txt produced a 8 character hexadecimal list. Thanks.
seq --help: "FORMAT must be suitable for printing one argument of type 'double'; it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point decimal numbers with maximum precision PREC, and to %g otherwise." The possible conversion specifiers are efgaEFGA.
this is significantly slower than the -w option that seq provides. and because it passes the number through printf, if you're not running the GNU versions (i.e. running mac native versions), you'll end up with scientific notation for larger numbers (roughly any ints > 6 digits will be in the x.xxxxxxe+0y format)
217

Easier still you can just do

for i in {00001..99999}; do echo $i done 

5 Comments

This works on Bash version 4.1.2 (CentOS 6) but fails in version 3.2.25 (CentOS 5). I do agree that this is much more aesthetically pleasing way to do it!
Works, but does not give zero padded strings on my bash
On a Mac (Bash 4.4), this doesn't pad the numbers. On CentOS & SuSE, it works great!
This works fine on macOS with Bash 4.4.23(1)-release
Note: If you're looping in order to run a command, and your command can accept multiple arguments, you might be better off running the command directly. e.g. It takes around 7 seconds for my computer to loop through for i in {0001..9000}; do; touch $i; done;, but is almost instant (and less code) if I just do: touch {0001..9000}
152

If the end of sequence has maximal length of padding (for example, if you want 5 digits and command is seq 1 10000), than you can use -w flag for seq - it adds padding itself.

seq -w 1 10 

would produce

01 02 03 04 05 06 07 08 09 10 

5 Comments

easy, because the padding depends on the max number you want to reach. If this is a parameter you never know beforehand how many zero you will end up with
This doesn't give a specified fixed length, just results which are all of the same length.
You can also do seq -w 001 009 to force length 3.
fresh seq installed in macOS does not have possiblity to do seq -w 001 009, so you are lucky it works for you
@m_messiah: The gnu version of seq on mac, installed with brew, from "coreutils" and available as gseq, works as mention by @isarandi, just the built-in bsd variant is too simple...
119

use printf with "%05d" e.g.

printf "%05d" 1 

Comments

32

Very simple using printf

[jaypal:~/Temp] printf "%05d\n" 1 00001 [jaypal:~/Temp] printf "%05d\n" 2 00002 

Comments

15

Use awk like this:

awk -v start=1 -v end=10 'BEGIN{for (i=start; i<=end; i++) printf("%05d\n", i)}' 

OUTPUT:

00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 

Update:

As pure bash alternative you can do this to get same output:

for i in {1..10} do printf "%05d\n" $i done 

This way you can avoid using an external program seq which is NOT available on all the flavors of *nix.

Comments

13

I pad output with more digits (zeros) than I need then use tail to only use the number of digits I am looking for. Notice that you have to use '6' in tail to get the last five digits :)

for i in $(seq 1 10) do RESULT=$(echo 00000$i | tail -c 6) echo $RESULT done 

2 Comments

If you wanted to use the correct number of places in the -c argument of tail, you could use 'echo -n 00000$i' as this stops it outputting a newline which is one of the chars tail is returning, hence it needing to be one higher in this answer. Depending what you're then doing, the newline, if left, might affect the results.
Using an external process to trim the variable in the loop is rather inefficient. You can use the shell's parameter expansion features instead. In Bash there is a facility for extracting a particular substring by index, or you can use the prefix and suffix substitutions with a pattern which matches the desired width, though it requires a bit of back-and-forth.
10

If you want N digits, add 10^N and delete the first digit.

for (( num=100; num<=105; num++ )) do echo ${num:1:3} done 

Output:

01 02 03 04 05 

Comments

9

One way without using external process forking is string manipulation, in a generic case it would look like this:

#start value CNT=1 for [whatever iterative loop, seq, cat, find...];do # number of 0s is at least the amount of decimals needed, simple concatenation TEMP="000000$CNT" # for example 6 digits zero padded, get the last 6 character of the string echo ${TEMP:(-6)} # increment, if the for loop doesn't provide the number directly TEMP=$(( TEMP + 1 )) done 

This works quite well on WSL as well, where forking is a really heavy operation. I had a 110000 files list, using printf "%06d" $NUM took over 1 minute, the solution above ran in about 1 second.

2 Comments

unfortunately my suggested edit to change the last line of the loop TEMP=$(( TEMP + 1 )) to CNT=$(( CNT + 1 )) got rejected, although increasing TEMP instead of CNT makes no sense! ...because TEMP gets overwritten with 0000001 at the beginning of the loop, you'll get an infinite loop.
beyond that, this answer (especially echo ${TEMP:(-6)}) is perfectly fine! - although there are also slightly different variants which may be a little bit more appropriate for different use cases.
7

Other way :

zeroos="000" echo for num in {99..105};do echo ${zeroos:${#num}:${#zeroos}}${num} done 

So simple function to convert any number would be:

function leading_zero(){ local num=$1 local zeroos=00000 echo ${zeroos:${#num}:${#zeroos}}${num} } 

1 Comment

if the number must not have more than 3 digits, i would recommend the first variant with the temp_num from askubuntu.com/a/1257316/354350. || although i personally prefer echo ${zeroos:0:-${#num}}${num} like mentioned in "Note 1".
4

TL;DR

$ seq 1 10 | awk '{printf("%05d\n", $1)}' 

Input(Pattern 1. Slow):

$ seq 1 10 | xargs -n 1 printf "%05d\n" 

Input(Pattern 2. Fast):

$ seq 1 10 | awk '{printf("%05d\n", $1)}' 

Output(same result in each case):

00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 

Explanation

I'd like to suggest the above patterns. These implementations can be used as a command so that we can use them again with ease. All you have to care about in these commands is the length of the numbers after being converted.(like changing the number %05d into %09d.) Plus, it's also applicable to other solutions such as the following. The example is too dependent on my environment, so your output might be different, but I think you can tell the usefulness easily.

$ wc -l * | awk '{printf("%05d\n", $1)}' 00007 00001 00001 00001 00013 00017 00001 00001 00001 00043 

And like this:

$ wc -l * | awk '{printf("%05d\n", $1)}' | sort | uniq 00001 00007 00013 00017 00043 

Moreover, if you write in this manner, we can also execute the commands asynchronously. (I found a nice article: https://www.dataart.com/en/blog/linux-pipes-tips-tricks)

disclaimer: I'm not sure of this, and I am not a *nix expert.

Performance test:

Super Slow:

$ time seq 1 1000 | xargs -n 1 printf "%09d\n" > test seq 1 1000 0.00s user 0.00s system 48% cpu 0.008 total xargs -n 1 printf "%09d\n" > test 1.14s user 2.17s system 84% cpu 3.929 total 

Relatively Fast:

for i in {1..1000} do printf "%09d\n" $i done $ time sh k.sh > test sh k.sh > test 0.01s user 0.01s system 74% cpu 0.021 total for i in {1..1000000} do printf "%09d\n" $i done $ time sh k.sh > test sh k.sh > test 7.10s user 1.52s system 99% cpu 8.669 total 

Fast:

$ time seq 1 1000 | awk '{printf("%09d\n", $1)}' > test seq 1 1000 0.00s user 0.00s system 47% cpu 0.008 total awk '{printf("%09d\n", $1)}' > test 0.00s user 0.00s system 52% cpu 0.009 total $ time seq 1 1000000 | awk '{printf("%09d\n", $1)}' > test seq 1 1000000 0.27s user 0.00s system 28% cpu 0.927 total awk '{printf("%09d\n", $1)}' > test 0.92s user 0.01s system 99% cpu 0.937 total 

If you have to implement the higher performance solution, probably it may require other techniques, not using the shell script.

Comments

4

Combining the seq -w answers with @tripleee 's comment on the question, you can do the following:

seq -w 1 1000 | while read -r i; do echo $i done 

As explained in the article @tripleee linked, this will read a single a single number from the pipe at a time, rather than loading the whole sequence into memory at once.

Note that I can't see any meaningful difference in speed compared to the for loop version, or the {0000..1000} version.

Can you avoid looping?

If you're doing this to run a command which can accept multiple arguments, it may be faster to run the command directly using multiple arguments and brace expansion than looping.

For example, on my system it takes 7 seconds to run the following loop:

for i in {0001..9001}; do touch $i; done 

But the following runs almost instantly:

touch {0001..9001} 

1 Comment

Nit: Extra semicolon between do and touch. Thanks for introducing me to the curlies seq-alternative!
3

This will work also:

for i in {0..9}{0..9}{0..9}{0..9} do echo "$i" done 

Comments

3

If you're just after padding numbers with zeros to achieve fixed length, just add the nearest multiple of 10 eg. for 2 digits, add 10^2, then remove the first 1 before displaying output.

This solution works to pad/format single numbers of any length, or a whole sequence of numbers using a for loop.

# Padding 0s zeros: # Pure bash without externals eg. awk, sed, seq, head, tail etc. # works with echo, no need for printf pad=100000 ;# 5 digit fixed for i in {0..99999}; do ((j=pad+i)) echo ${j#?} done 

Tested on Mac OSX 10.6.8, Bash ver 3.2.48

Comments

1

you don't need awk for that — either seq or jot alone suffices :

% seq -f '%05.f' 6 # bsd-seq 00001 00002 00003 00004 00005 00006 % gseq -f '%05.f' 6 # gnu-seq 00001 00002 00003 00004 00005 00006 % jot -w '%05.f' 6 00001 00002 00003 00004 00005 00006 

…… unless you're going into bigint territory :

% gawk -Mbe ' function __(_,___) { return +_<+___?___:_ } BEGIN { _+=_^=_<_ ____="%0*.f\n" } { ___=__($--_, !+$++_) _____=__(++_+--_, length(______=+$NF)) do { printf(____,_____,___) } while (___++<______) }' <<< '999999999999999999996 1000000000000000000003' 0999999999999999999996 0999999999999999999997 0999999999999999999998 0999999999999999999999 1000000000000000000000 1000000000000000000001 1000000000000000000002 1000000000000000000003 

——————————————————————————————————————————————————

If you need to print out a HUGE range of numbers, then this approach maybe a bit faster -

  • printing out every integer from 1 to 1 million, left-zero-padded to 9-digits wide, in 0.049s

  • *caveat : I didn't have the spare time to make it cover all input ranges :: it's just a proof of concept accepting increments of powers of 10

——————————————————————————————————————————————————

 ( time ( LC_ALL=C mawk2 ' function jot(____,_______,_____,_,__,___,______) { if(____==(____^!____)) { return +____<+_______\ ? sprintf("%0*.f",_______,____)\ : +____ } _______= (_______-=____=length(____)-\ (_=!(_<_)))<+_ \ ? "" \ : sprintf("%0*.f",_______,!_) __=_= (!(__=_+=_+_))(__=(-+--_)+(__+=_)^++_)\ (__+=_=(((_--^_--+_++)^++_-_^!_)/_))(__+_) _____= "." gsub(_____,"\\&&",__) ____—- do { gsub(_____,__,_) _____=_____"." } while(—____) gsub(_____,(_______)"&\n",_) sub("^[^\n]+[\n]","",_) sub(".$",""~"",_______) return \ (_)(_______)\ sprintf("%0*.f",length(_____),__<__) } { print jot($1,$2) }' <<< '10000000 9' ) | pvE9 ) |xxh128sum |ggXy3 | lgp3 sleep 2 ( time ( LC_ALL=C jot 1000000 | LC_ALL=C mawk2 '{ printf("%09.f\n", $1) }' ) | pvE9 ) |xxh128sum |ggXy3 | lgp3 out9: 9.54MiB 0:00:00 [ 275MiB/s] [ 275MiB/s] [<=> ] ( LC_ALL=C mawk2 <<< '1000000 9'; ) 0.04s user 0.01s system 93% cpu 0.049 total e0491043bdb4c8bc16769072f3b71f98 stdin out9: 9.54MiB 0:00:00 [36.5MiB/s] [36.5MiB/s] [ <=> ] ( LC_ALL=C jot 1000000 | LC_ALL=C mawk2 '{printf("%09.f\n", $1)}'; ) 0.43s user 0.01s system 158% cpu 0.275 total e0491043bdb4c8bc16769072f3b71f98 stdin 

By the time you're doing 10 million, the time differences become noticeable :

 out9: 95.4MiB 0:00:00 [ 216MiB/s] [ 216MiB/s] [<=> ] ( LC_ALL=C mawk2 <<< '10000000 9'; ) 0.38s user 0.06s system 95% cpu 0.458 total be3ed6c8e9ee947e5ba4ce51af753663 stdin out9: 95.4MiB 0:00:02 [36.3MiB/s] [36.3MiB/s] [ <=> ] ( LC_ALL=C jot 10000000 | LC_ALL=C mawk2 '{printf("%09.f\n", $1)}'; ) 4.30s user 0.04s system 164% cpu 2.638 total be3ed6c8e9ee947e5ba4ce51af753663 stdin out9: 95.4MiB 0:00:02 [35.2MiB/s] [35.2MiB/s] [ <=> ] ( LC_ALL=C python3 -c '__=1; ___=10**7; [ print("{0:09d}".format(_)) for _ in range(__,___+__) ]' ) | pvE9 ) | xxh128sum |ggXy3 | lgp3 ; ) 2.68s user 0.04s system 99% cpu 2.725 total be3ed6c8e9ee947e5ba4ce51af753663 stdin 

Comments

0

1.) Create a sequence of numbers 'seq' from 1 to 1000, and fix the width '-w' (width is determined by length of ending number, in this case 4 digits for 1000).

2.) Also, select which numbers you want using 'sed -n' (in this case, we select numbers 1-100).

3.) 'echo' out each number. Numbers are stored in the variable 'i', accessed using the '$'.

Pros: This code is pretty clean.

Cons: 'seq' isn't native to all Linux systems (as I understand)

for i in `seq -w 1 1000 | sed -n '1,100p'`; do echo $i; done 

Comments

0

Keep it simple.

# # Pad to 8 characters # for I in 0 9 99 999 9999 99999 999999 9999999 99999999 ; do PAD="00000000$I" echo "${PAD##${PAD%%????????}}" done 

Output:

00000000 00000009 00000099 00000999 00009999 00099999 00999999 09999999 99999999 

Comments

-2

Sample use for zero pad numeric sequence

# Rename directory files numericaly n='0000000000' for f in $(ls); do num=$(seq -w $n 000000000$n | tail -c 9) mv $f $num.new (( n++ )) done 

1 Comment

Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.