Differences between revisions 1 and 21 (spanning 20 versions)
Revision 1 as of 2007-05-02 23:04:00
Size: 1899
Editor: redondos
Comment:
Revision 21 as of 2012-02-01 15:38:16
Size: 4435
Editor: GreyCat
Comment: it's a useful trick for bash < 4 (I'll add a warning)
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq18)]] <<Anchor(faq18)>>
Line 7: Line 7:
    # Bash
Line 15: Line 16:
00
01
02
03
[...]
   01
   02
   03
   [...]
Line 22: Line 22:
This gets tedious for large sequences, but there are other ways, too. If the command {{{seq}}} is available, you can use it as follows: The same thing can be accomplished more concisely like this:
Line 24: Line 25:
    seq -w 1 10     # Bash
    for i in 0{1..9} 10
    do
        echo $i
    done
Line 27: Line 32:
or, for arbitrary numbers of leading zeros (here: 3): Another example, for output of 0000 to 0034:
Line 30: Line 35:
    seq -f "%03g" 1 10     # Bash
    for i in {000{0..9},00{10..34}}
    do
        echo $i
    done

    # using the outer brace instead of just adding them one next to the other
    # allows to use the expansion, for instance, like this:
    wget 'http://foo.com/adir/thepages'{000{0..9},00{10..34}}'.html'
Line 33: Line 46:
If you have the {{{printf}}} command (which is a Bash builtin, and is also POSIX standard), it can be used to format a number, too: Some may prefer the following Q&D solution (producing "001" through "015"):
Line 36: Line 49:
    # Bash
    for i in {1000..1015}
    do
      i=${i:1}
      echo $i
    done
}}}


This gets tedious for large sequences, but there are other ways, too. If you have the {{{printf}}} command (which is a Bash builtin, and is also POSIX standard), it can be used to format a number:
{{{
    # Bash
Line 42: Line 67:
The KornShell and KornShell93 have the {{{typeset}}} command to specify the number of leading zeros: In Bash 3, you can use ranges inside brace expansion. Also, since {{{printf}}} will implicitly loop if given more arguments than format specifiers, you can simplify this enormously:
{{{
   # Bash 3
   printf "%03d\n" {1..300}
}}}

If you don't know in advance what the starting and ending values are:
{{{
   # Bash 3
   # START and END are variables containing integers
   eval printf '"%03d\n"' {$START..$END}
}}}

The `eval` is needed here because you cannot have variables in a brace expansion -- only constants. The extra quotes are required by the `eval` so that our `\n` isn't changed to an `n`. Given how messy that `eval` solution is, please give serious thought to using the `for` loop instead.

Also, bash version 4 allows zero-padding in its brace expansion:
Line 45: Line 85:
    # Bash 4
    echo {01..10}

    for i in {01..10}; do ...
}}}

The KornShell has the {{{typeset}}} command to specify the number of leading zeros:
{{{
    # Korn
Line 50: Line 99:
Finally, the following example works with any BourneShell derived shell to zero-pad each line to three bytes:
If the command {{{seq(1)}}} is available (it's part of GNU sh-utils/coreutils), you can use it as follows:
Line 53: Line 101:
i=0
while test $i -le 10
do
    echo "00$i"
    i=`expr $i + 1`
done |
    sed 's/.*\(...\)$/\1/g'
    seq -w 1 10
Line 62: Line 104:
In this example, the number of '.' inside the parentheses in the {{{sed}}} statement determins how many total bytes from the {{{echo}}} command (at the end of each line) will be kept and printed.

One more addendum: in Bash 3, you can use:
or, for arbitrary numbers of leading zeros (here: 3):
Line 66: Line 106:
printf "%03d \n" {1..300}     seq -f "%03g" 1 10
Line 69: Line 109:
Which is slightly easier in some cases.

Also you can use the {{{printf}}} command with xargs and wget to fetch files:
Combining {{{printf}}} with {{{seq(1)}}}, you can do things like this:
Line 74: Line 111:
printf "%03d \n" {$START..$END} | xargs -i% wget $LOCATION/%    # POSIX shell, GNU utilities
   printf "%03d\n" $(seq 300)
Line 77: Line 115:
Sometimes a good solution. (That may be helpful if your version of {{{seq(1)}}} lacks {{{printf}}}-style format specifiers. Since it's a nonstandard external tool, it's good to keep your options open.)
Line 79: Line 117:
I found that on bash 2 you can nest seq in back ticks and this will work as well. Be warned however that `seq` might be considered bad style; it's even mentioned in [[BashGuide/Practices#Don.27t_Ever_Do_These|Don't Ever Do These]].
Line 81: Line 119:
Finally, the following example works with any BourneShell derived shell (which also has `expr` and `sed`) to zero-pad each line to three bytes:
Line 82: Line 121:
printf "%03d \n" `seq 300`    # Bourne
   i=0
   while test $i -le 10
   do
       echo "00$i"
       i=`expr $i + 1`
   done |
       sed 's/.*\(...\)$/\1/g'
Line 84: Line 130:

In this example, the number of '.' inside the parentheses in the {{{sed}}} command determines how many total bytes from the {{{echo}}} command (at the end of each line) will be kept and printed.

But if you're going to rely on an external Unix command, you might as well just do the whole thing in `awk` in the first place:
{{{
   # Bourne
   # COUNT variable contains an integer
   awk "BEGIN {for (i=1;i<$COUNT;i++) {printf(\"%03d\n\",i)} }"
}}}

Now, since the number one reason this question is asked is for downloading images in bulk, you can use the examples above with {{{xargs(1)}}} and {{{wget(1)}}} to fetch files:
{{{
   almost any example above | xargs -i% wget $LOCATION/%
}}}

The `xargs -i%` will read a line of input at a time, and replace the `%` at the end of the command with the input.

Or, a simpler example using a `for` loop:
{{{
   # Bash 3
   for i in {1..100}; do
      wget "$prefix$(printf %03d $i).jpg"
      # other commands
   done
}}}

----
CategoryShell

How can I use numbers with leading zeros in a loop, e.g. 01, 02?

As always, there are different ways to solve the problem, each with its own advantages and disadvantages.

If there are not many numbers, BraceExpansion can be used:

    # Bash
    for i in 0{1,2,3,4,5,6,7,8,9} 10
    do
        echo $i
    done

Output:

   01
   02
   03
   [...]

The same thing can be accomplished more concisely like this:

    # Bash
    for i in 0{1..9} 10
    do
        echo $i
    done

Another example, for output of 0000 to 0034:

    # Bash
    for i in {000{0..9},00{10..34}}
    do
        echo $i
    done

    # using the outer brace instead of just adding them one next to the other
    # allows to use the expansion, for instance, like this:
    wget 'http://foo.com/adir/thepages'{000{0..9},00{10..34}}'.html'

Some may prefer the following Q&D solution (producing "001" through "015"):

    # Bash
    for i in {1000..1015}
    do
      i=${i:1}
      echo $i
    done

This gets tedious for large sequences, but there are other ways, too. If you have the printf command (which is a Bash builtin, and is also POSIX standard), it can be used to format a number:

    # Bash
    for ((i=1; i<=10; i++))
    do
        printf "%02d " "$i"
    done

In Bash 3, you can use ranges inside brace expansion. Also, since printf will implicitly loop if given more arguments than format specifiers, you can simplify this enormously:

   # Bash 3
   printf "%03d\n" {1..300}

If you don't know in advance what the starting and ending values are:

   # Bash 3
   # START and END are variables containing integers
   eval printf '"%03d\n"' {$START..$END}

The eval is needed here because you cannot have variables in a brace expansion -- only constants. The extra quotes are required by the eval so that our \n isn't changed to an n. Given how messy that eval solution is, please give serious thought to using the for loop instead.

Also, bash version 4 allows zero-padding in its brace expansion:

    # Bash 4
    echo {01..10}

    for i in {01..10}; do ...

The KornShell has the typeset command to specify the number of leading zeros:

    # Korn
    $ typeset -Z3 i=4
    $ echo $i
    004

If the command seq(1) is available (it's part of GNU sh-utils/coreutils), you can use it as follows:

    seq -w 1 10

or, for arbitrary numbers of leading zeros (here: 3):

    seq -f "%03g" 1 10

Combining printf with seq(1), you can do things like this:

   # POSIX shell, GNU utilities
   printf "%03d\n" $(seq 300)

(That may be helpful if your version of seq(1) lacks printf-style format specifiers. Since it's a nonstandard external tool, it's good to keep your options open.)

Be warned however that seq might be considered bad style; it's even mentioned in Don't Ever Do These.

Finally, the following example works with any BourneShell derived shell (which also has expr and sed) to zero-pad each line to three bytes:

   # Bourne
   i=0
   while test $i -le 10
   do
       echo "00$i"
       i=`expr $i + 1`
   done |
       sed 's/.*\(...\)$/\1/g'

In this example, the number of '.' inside the parentheses in the sed command determines how many total bytes from the echo command (at the end of each line) will be kept and printed.

But if you're going to rely on an external Unix command, you might as well just do the whole thing in awk in the first place:

   # Bourne
   # COUNT variable contains an integer
   awk "BEGIN {for (i=1;i<$COUNT;i++) {printf(\"%03d\n\",i)} }"

Now, since the number one reason this question is asked is for downloading images in bulk, you can use the examples above with xargs(1) and wget(1) to fetch files:

   almost any example above | xargs -i% wget $LOCATION/%

The xargs -i% will read a line of input at a time, and replace the % at the end of the command with the input.

Or, a simpler example using a for loop:

   # Bash 3
   for i in {1..100}; do
      wget "$prefix$(printf %03d $i).jpg"
      # other commands
   done


CategoryShell

BashFAQ/018 (last edited 2019-08-21 16:24:29 by GreyCat)