220

I have a basic number for loop which increments the variable num by 1 over each iteration...

for (( num=1; num<=5; num++ ))
do
 echo $num
done

Which outputs:

1
2
3
4
5

I'm trying to make it produce the output (add leading zero before $num):

01
02
03
04
05

Without doing:

echo 0$num
kenorb
  • 155,785
  • 88
  • 678
  • 743
Bruce Blacklaws
  • 2,338
  • 2
  • 15
  • 14

7 Answers7

387

Use the following syntax:

$ for i in {01..05}; do echo "$i"; done
01
02
03
04
05

Disclaimer: Leading zeros only work in >=bash-4.

If you want to use printf, nothing prevents you from putting its result in a variable for further use:

$ foo=$(printf "%02d" 5)
$ echo "${foo}"
05
Vampire
  • 35,631
  • 4
  • 76
  • 102
Adrian Frühwirth
  • 42,970
  • 10
  • 60
  • 71
  • 3
    Best answer, and it avoids using clumsy external commands (like 'seq', not necessary in proper Bash). Helped me. – Scott Prive Jan 28 '15 at 20:31
  • 8
    Unfortunately the {01..05} doesn't work for older versions of bash. I'm on a server with bash 3.2.51 and that will print the numbers without leading zeroes. On my desktop with 4.2.37 it does work well though. – thomanski Feb 13 '15 at 16:04
  • @thomanski Fair point, the [Changelog](http://tiswww.case.edu/php/chet/bash/CHANGES) states that this feature got introduced with `bash-4.0-alpha`: *Brace expansion now allows zero-padding of expanded numeric values and will add the proper number of zeroes to make sure all values contain he same number of digits.)*. – Adrian Frühwirth Feb 13 '15 at 18:56
  • doesn't work for e.g. {01..10} though – Matt Dec 21 '18 at 13:05
  • 1
    @Matt What exactly does not work? As long as you have a recent enough version of `bash` this works fine. – Adrian Frühwirth Jan 10 '19 at 08:34
  • Works like a charm, thanks a lot! Here is my version: `for i in \`seq 1 1 50\`; echo $(printf "%02d" $i); done`, which will output `01 02 ... 50`. – SRG Jan 31 '20 at 19:21
  • 3
    @SRG `seq` can do the padding itself if you want to pad to the widest numbers width using `-w`, besides that your example is invalid as it misses the `do`. Here is how you can do it: `for i in \`seq -w 1 50\`; do echo $i; done` – Vampire Jul 13 '20 at 10:39
  • 1
    @Vampire I cannot edit my comment to fix the missing `do`, but thanks for the alternative, sounds interesting! – SRG Jul 14 '20 at 15:22
  • m=4;printf -v m "%02d" $m;echo $m – zzapper Sep 23 '20 at 10:01
79

seq -w will detect the max input width and normalize the width of the output.

for num in $(seq -w 01 05); do
    ...
done

At time of writing this didn't work on the newest versions of OSX, so you can either install macports and use its version of seq, or you can set the format explicitly:

seq -f '%02g' 1 3
    01
    02
    03

But given the ugliness of format specifications for such a simple problem, I prefer the solution Henk and Adrian gave, which just uses Bash. Apple can't screw this up since there's no generic "unix" version of Bash:

echo {01..05}

Or:

for number in {01..05}; do ...; done
piojo
  • 6,351
  • 1
  • 26
  • 36
  • 3
    Just fyi `seq` is not present on OSX – anubhava Aug 27 '13 at 09:29
  • 2
    It's on my machine (OS X 10.7.5). The man page only dates to 2010, so it looks to be a (relatively) recent addition. – chepner Aug 27 '13 at 13:00
  • 1
    doesnt work on mac os sierra for 01 to 05. but works for 01 to 10. – at0mzk Oct 06 '16 at 02:34
  • 3
    Try giving seq a format string. `seq -f '%02g' 1 5` – blarf Oct 06 '16 at 20:40
  • @blarf I mentioned that in the middle of my answer, but I'm generally opposed to format strings for very simple formats like this. – piojo Oct 12 '16 at 13:32
  • 1
    `seq -w` really does the job without clutter! – katamayros Oct 19 '17 at 09:46
  • what is the difference between `'%02g'` and `'%02d'` (which are others using plain `printf` are suggesting)? – DJCrashdummy Aug 19 '20 at 09:00
  • @DJCrashdummy According to the docs, `seq` doesn't support `%d`. The format flags seem a bit different than for `printf`. http://www.gnu.org/software/coreutils/manual/html_node/seq-invocation.html#seq-invocation – piojo Aug 20 '20 at 02:18
  • This solution with "seq -f '%02g' 1 3" is very useful when "1 and 3" are unformatted input variables, like "seq -f '%02g' ${dia_ini} ${dia_fim}" – Newton_Jose Mar 04 '21 at 12:42
  • The way to go! This should be the accepted answer for really universal padding with zeros (e.g. `-f '%04g'` will properly pad 10 to "0010" and not to "000010"). – mirekphd Aug 15 '21 at 12:07
  • I really like the seq option, particularly when the user has to pass conditional variables into a loop that disqualifies the use of brace expansion. The C-style loops can also be used to execute this, but seq feels cleaner to me. – TornadoEric Mar 07 '23 at 16:27
56

Use printf command to have 0 padding:

printf "%02d\n" $num

Your for loop will be like this:

for (( num=1; num<=5; num++ )); do printf "%02d\n" $num; done
01
02
03
04
05
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    I'm not interested in outputting it to the screen (that's what printf is mainly used for, right?) The variable $num is going to be used as a parameter for another program but let me see what I can do with this. – Bruce Blacklaws Aug 27 '13 at 08:13
  • Please explain clearly what you are trying to do than I can suggest a better suited answer. – anubhava Aug 27 '13 at 08:36
  • 5
    @BruceBlacklaws `printf` in the shell is used for creating strings from a list of arguments. Output to screen is just the default. In fact, shell pipelines are the ultimate example of functional programming -- just input, output, no side-effects. ;-) – Henk Langeveld Aug 27 '13 at 19:54
31

I'm not interested in outputting it to the screen (that's what printf is mainly used for, right?) The variable $num is going to be used as a parameter for another program but let me see what I can do with this.

You can still use printf:

for num in {1..5}
do
   value=$(printf "%02d" $num)
   ... Use $value for your purposes
done
David W.
  • 105,218
  • 39
  • 216
  • 337
  • 5
    This works but spawning subshell is expensive. For big sequences it may take very long time. – rr- Apr 04 '15 at 21:16
14

From bash 4.0 onward, you can use Brace Expansion with fixed length strings. See below for the original announcement.

It will do just what you need, and does not require anything external to the shell.

$ echo {01..05}
01 02 03 04 05

for num in {01..05}
do
  echo $num
done
01
02
03
04
05

CHANGES, release bash-4.0, section 3

This is a terse description of the new features added to bash-4.0 since the release of bash-3.2.

. . .

z. Brace expansion now allows zero-padding of expanded numeric values and will add the proper number of zeroes to make sure all values contain the same number of digits.

Community
  • 1
  • 1
Henk Langeveld
  • 8,088
  • 1
  • 43
  • 57
  • I know I did not specify which version of BASH and what platform it's running on but this flat fails on GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16) – Bruce Blacklaws Mar 15 '17 at 13:09
  • @BruceBlacklaws Correct. The fixed length numbers were added in 4.0. Included the restriction. – Henk Langeveld Mar 16 '17 at 23:18
11

why not printf '%02d' $num? See help printf for this internal bash command.

Benoit
  • 76,634
  • 23
  • 210
  • 236
3

Just a note: I have experienced different behaviours on different versions of bash:

  • version 3.1.17(1)-release-(x86_64-suse-linux) and
  • Version 4.1.17(9)-release (x86_64-unknown-cygwin))

for the former (3.1) for nn in (00..99) ; do ... works but for nn in (000..999) ; do ... does not work both will work on version 4.1 ; haven't tested printf behaviour (bash --version gave the version info)

Cheers, Jan

jd_doubley
  • 31
  • 1