Zero Padding in Bash

April 30th, 2007 § 32 comments

This is as much a reminder for myself as anything. I needed to create 31 folders named 00 through 31 for each day of the month in a web-directory used to store audio files from CAPE-1 transmissions. The first thought that came to mind was to write a quick one-liner bash command/script to create those folders. So, without much thought, I quickly typed the following into a shell on my web server:

for ((x=1;x<=31;x+=1)); do mkdir $x; done

OK, well that did create 31 directories, but the first 9 were named 1, 2, 3, etc. instead of 01, 02, 03 like I wanted. After a quick search on zero-padding in bash it seemed no one had posted anything about it except to say it was tricky. Being a C programmer before a bash script writer, I thought to myself, “Wouldn’t it be nice if I could use a printf() statement in bash?” Then I remembered that you can, only usually it is used for formatting user interface statements. I could not think of any reason it would not work to deliver the argument to a mkdir statement, so I tried:


$ for ((x=1;x< =31;x+=1)); do mkdir `printf "%02d" $x`; done
$ ls
01 03 05 07 09 11 13 15 17 19 21 23 25 27 29 31
02 04 06 08 10 12 14 16 18 20 22 24 26 28 30

And it works! Breaking the command down gives a standard bash for loop with the mkdir command as the internal command inside the loop. As an argument to the mkdir command, however, is a printf statement which given the formatting prints a zero-padded (the 0 after %) two-character long (the 2 after the 0) integer, x. Remember, the `s are back-ticks (below the tilde on most keyboards), not single quotation marks (‘).

It seems like there should be a better way of doing this, but quickest way I found to add zero padding to a number in bash is to use the provided printf which uses the same formatting as the C language printf statement. More usage and examples are available at the ss64.org bash printf reference page.

§ 32 Responses to Zero Padding in Bash"

  • gabe says:

    Man, this is great!
    You have no idea how often it annoyed me that bash can’t do zero-padding, but I didn’t know about the bash’s printf command. Thanks a lot, this really made my day! I need to number thousands of files (output from a scientific simulation) and then concatenate them later in the right order, so I really need zero-padded numbers in the filename. I found your blog-entry with Google’s “I’m feeling lucky” and the searchwords “zero padded integers BASH”…
    Maybe there should be an easier way to do this in bash, but it works pretty well for me this way.
    Thanks again for posting this, you’ve helped out scientific progress ;-)

  • Jonathan says:

    gabe,

    Glad you found it helpful. It does seem that there should be a cleaner way, but this was the best I found.

  • Nikhil says:

    Thanks! It is true that searching the web doesn’t give much help for padding numbers in bash! Your article was really helpful!

  • John says:

    For generating this sort of thing seq can generate zero padded sequences of numbers…

    for i in `seq -f ‘%02g’ 1 31` ; do mkdir $i ; done

  • al says:

    Starting to work on some BASH stuff the other day and this saved my sanity. Thanks!

  • David says:

    seq has an option to zero-pad (-w) so you can just do this:

    seq -w 1 31

  • snakehsu says:

    Thanks for the tips. For your particular case you can use seq no problem. But sometimes you may need to use these numbers as array index, which is my case, in which I cannot use seq -w as Bash consider 01, 02 … as octet numbers so 08 and 09 would be “illegal” index. So I used seq without -w as index and used your printf method to generate zero paddings when I need them in the filename.

  • William Tracy says:

    Thank-you.

    Thank-you, thank-you.

  • pedro says:

    Great stuff, I used to program in C and C++ but never really realised how printf() could fit in shell commands/scripts.

    Now I do! Good on ya, thanks

  • SirPavlova says:

    For anyone wanting to do this using jot, try “jot -w %03d 10 1″ for 001 to 010, etc. Hopefully this saves one or two people a trip to the man pages.

  • gxx says:

    Thanks SirPavlova, that was exactly what I was looking for…

  • Binny V A says:

    Thanks! I was going to write a script for this – you saved my time.

  • mardson says:

    thank you all. much time saved, much work accomplished, minimal effort expended ;)

  • Thul Dai says:

    Thanks a lot, very useful.

    What I often do as a workaround is add 100 (or 1000 as it were) so my ensemble runs (and respective folders, files, …) are numbered from 101 to 150 instead of 1 to 50. That works also easily in Fortran and other funky languages.

    TD

  • Anal says:

    Thank you so much.

  • Mohsen says:

    Many thanks.
    and thanks John and David for tips on seq

  • Logan says:

    for ((i=1;i<=11;i+=1)); do
    s=`printf “%02d” $i`;
    echo $s;
    done

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11

  • Mike H says:

    thanks a plenty for the printf, jot and sed info guys.. now I have my script sorted and saving me a huge amount of time when it comes to the image files I am processing.

    cheers

  • jimmy says:

    neat tip — thanks!!

  • Hernan says:

    Calling printf will create a new process every time; this may be a little slow for some applications. A better performing example:

    #!/bin/bash

    (( million = 1000000 ))
    (( index = 1 ))

    while [ $index -lt 500000 ]
    do
    (( printme = index + million ))
    (( index = index + 1 ))
    echo ${printme:1}
    done

    (See the “Shell Parameter Expansion” section of the bash manual for an explanation of what ${printme:1} does.)

    On my machine at least (running Debian 4.0; bash v3.1.17) this is 12x faster than the printf version.

    The “printf” solution is way more legible, of course.

  • I use this rather “simple” solution, somewhat similar to Heman’s, though it requires >= bash 2.02:

    function zeropad {
    expo=$((10 ** $2))
    [ $1 -gt $expo ] &formatd=$(($1 + $expo))
    echo ${formatd:1}
    }

    Example:
    zeropad 59 10
    0000000059

  • It would appear this blog software messed with my comment :(

    The 3rd and 4th lines were merged into one. They should be:

    [ $1 -gt $expo ] AMPAMP { echo $1; return; }
    formatd=$(($1 + $expo))

    where AMPAMP is 2 ampersands.

  • A grateful visitor says:

    ZOMG, you have no idea how much this helped a ‘nix n00b like me.

    2 years on and this webpage is still helping people.

    Thanks a lot!

  • Benoît Terradillos says:

    Even more simple (based on Hernan solution):

    padding=”00000″ # put as much zeros as you want.
    for ((i=0; i<31; i+=1))
    do
    echo ${padding:${#i}}$i
    done

  • cdaaawg says:

    @Benoit Terradillos
    Running your code under bash 3.2.39(1), I get the following output:
    00000″0
    00000″1
    00000″2
    00000″3
    00000″4
    00000″5
    00000″6
    00000″7
    00000″8
    00000″9
    0000″10

    Bizarre!

    – Carl

  • Jonathan says:

    @cdaaawg: It works if you type it yourself. The blog software replaced the second quotation mark of the padding variable in Benoit’s post with a fancy one.

  • pbrisbin says:

    Just a headsup, zero-padding was recently added to brace expansion (among other tricks).

    this is equivalent bash v4 code:

    for x in $(echo {01..32}); do mkdir $x; done

  • Gengis says:

    “zeropad function”, works like a charm to me, thank you very much.

    In order to return something beyond my thanks, here is my script:

    #!/bin/bash
    EXPECTED_ARGS=3
    COUNTEROFSHOTS=0
    COUNTER=0
    return=0
    imageName=0

    # define usage function
    usage(){
    echo “Usage: $0 shortNameForDatabase startNumber numberOfFiguresForEachNumber”
    exit 1
    }

    help(){
    echo
    echo “This script help the user in order to take multiple source images for the software X.”
    echo
    echo “1. Please make sure script Y is properly calibrated.”
    echo “2. Press any key in order to take one shot, Ctrl+c to quit”
    echo
    }

    function zeropad {
    expo=$((10 ** $2))
    [ $1 -gt $expo ] &formatd=$(($1 + $expo))
    return=${formatd:1}
    }

    if [ $# -ne $EXPECTED_ARGS ]
    then
    usage
    echo -n “Press any key to take shot!”
    else
    COUNTER=$2
    help
    while [ 1 ]
    do
    read var_name
    zeropad $COUNTER $3
    imageName=$1$return.png
    echo ./Y $imageName
    COUNTER=$((COUNTER+1))
    COUNTEROFSHOTS=$((COUNTEROFSHOTS+1))
    echo -n $COUNTEROFSHOTS”.”$imageName.” done. Press any key to take shot!”
    done
    fi

  • Duber says:

    Thanks, this was useful for me

  • Leigh Orf says:

    The printf command is a most awesome solution for me, thanks!

    seq -w is fine unless you want to do mathematical operations on the zero-padded number. Bash interprets a leading zero on an integer constant as an octal number, which messes things up if you want to do decimal operations on that constant. In fact, for a long time I used the korn shell (ksh or pdksh) for my scripts (they use ImageMagick to add hh:mm:ss timestamps to images) because the korn shell does not interpret constants with a leading zero as an octal, it just treats them like leading decimal zeroes.

    But now that I have found the printf statement, I can seq without -w, do my math and still create zero-padded strings when I need them. Yay!

  • MetalElf0 says:

    Thanks for sharing, these articles are the ones that save you a lot of time. Good work!

  • Wyatt says:

    It’s even more simple than pbrisbin stated. In bash 4 you can just do this:
    mkdir {01..31}

    You can even do silly things like:
    mkdir -p month{01..12}/day{01..31}
    touch month{01..12}/day{01..31}/.keep

    It’s really stupidly powerful. Cheers!

What's this?

You are currently reading Zero Padding in Bash at Jonathan Wagner.

meta