Differences between revisions 3 and 23 (spanning 20 versions)
Revision 3 as of 2008-03-10 10:52:49
Size: 2625
Editor: 134
Comment: seq might be considered bad style
Revision 23 as of 2014-05-05 11:47:15
Size: 5316
Editor: geirha
Comment: pointless to use the s///g when the regex is anchored.
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq18)]]
<<Anchor(faq18)>>
Line 6: Line 5:
If there are not many numbers, BraceExpansion can be used: Bash version 4 allows zero-padding and ranges in its BraceExpansion:
Line 9: Line 8:
    # Bash 4
    echo {01..10}

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

All of the other solutions on this page will assume Bash earlier than 4.0, or a non-Bash shell.

If there are not many numbers, BraceExpansion can be used:
{{{
    # Bash
Line 14: Line 24:
Output:
In Bash 3, you can use ranges inside brace expansion (but not zero-padding). Thus, the same thing can be accomplished more concisely like this:
Line 17: Line 28:
   00
   01
   02
   03
   [...]
    # Bash 3
    for i in 0{1..9} 10
    do
        echo $i
    done
Line 23: Line 34:
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:
Another example, for output of 0000 to 0034:
Line 26: Line 38:
    for ((i=1; i<=10; i++)) # Bash 2 for-loop syntax     # Bash 3
    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 quick & dirty solution (producing "001" through "015"):

{{{
    # Bash 3
    for i in {1000..1015}
    do
      echo "${i:1}" # or "${i#1}"
    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++))
Line 31: Line 68:
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:
Line 33: Line 69:
Also, since {{{printf}}} will implicitly loop if given more arguments than format specifiers, you can simplify this enormously:
Line 34: Line 71:
   printf "%03d\n" {1..300}     # Bash 3 brace expansion    # Bash 3
printf "%03d\n" {1..300}
Line 36: Line 74:
The KornShell and KornShell93 have the {{{typeset}}} command to specify the number of leading zeros:
Line 38: Line 75:
If you don't know in advance what the starting and ending values are:
Line 39: Line 77:
   # 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.

The KornShell has the {{{typeset}}} command to specify the number of leading zeros:
{{{
    # Korn
Line 43: Line 91:
Line 44: Line 93:
Line 48: Line 96:
Line 49: Line 98:
Line 53: Line 101:
Line 54: Line 103:
Line 56: Line 104:
   # POSIX shell, GNU utilities
Line 58: Line 107:
(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 60: Line 108:
Be warned however that seq might be considered bad style, it's even mentioned in ["Don't Ever Do These"]. (That may be helpful if you are not using Bash, but you have `seq(1)`, and your version of {{{seq(1)}}} lacks {{{printf}}}-style format specifiers. That's a pretty odd set of restrictions, but I suppose it's theoretically possible. Since `seq` is a nonstandard external tool, it's good to keep your options open.)
Line 62: Line 110:
Finally, the following example works with any BourneShell derived shell to zero-pad each line to three bytes: Be warned however that using `seq` might be considered bad style; it's even mentioned in [[BashGuide/Practices#Don.27t_Ever_Do_These|Don't Ever Do These]].
Line 64: Line 112:
Some BSD-derived systems have `jot(1)` instead of `seq(1)`. In accordance with the glorious tradition of Unix, it has a completely incompatible syntax:
Line 65: Line 114:
   # POSIX shell, OpenBSD et al.
   printf "%02d\n" $(jot 10 1)

   # Bourne shell, OpenBSD (at least)
   jot -w %02d 10 1
}}}

Finally, the following example works with any BourneShell derived shell (and where `expr` and `sed` are also available) to zero-pad each line to three bytes:
{{{
   # Bourne
Line 66: Line 125:
   while test $i -le 10    while test "$i" -le 10
Line 69: Line 128:
       i=`expr $i + 1`        i=`expr "$i" + 1`
Line 71: Line 130:
       sed 's/.*\(...\)$/\1/g'        sed 's/.*\(...\)$/\1/'
Line 73: Line 132:
Line 75: Line 135:
Now, since the number one reason this question is asked is for downloading images in bulk, you can use the {{{printf}}} command with {{{xargs(1)}}} and {{{wget(1)}}} to fetch files: 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 -v count="$count" 'BEGIN {for (i=1;i<=count;i++) {printf("%03d\n",i)} }'
Line 77: Line 141:
   # Bourne, with Solaris's decrepit and useless awk:
   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:
Line 78: Line 149:
   printf "%03d\n" {$START..$END} | xargs -i% wget $LOCATION/%    almost any example above | xargs -i% wget $LOCATION/%
Line 80: Line 151:
Or, in a slightly more general case:
Line 82: Line 152:
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:
Line 83: Line 156:
   # Bash 3
Line 85: Line 159:
      # other commands       sleep 5
Line 88: Line 162:

Or, avoiding the subshells (requires bash 3.1):
{{{
   # Bash 3.1
   for i in {1..100}; do
      printf -v n %03d $i
      wget "$prefix$n.jpg"
      sleep 5
   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.

Bash version 4 allows zero-padding and ranges in its BraceExpansion:

    # Bash 4
    echo {01..10}

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

All of the other solutions on this page will assume Bash earlier than 4.0, or a non-Bash shell.

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

In Bash 3, you can use ranges inside brace expansion (but not zero-padding). Thus, the same thing can be accomplished more concisely like this:

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

Another example, for output of 0000 to 0034:

    # Bash 3
    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 quick & dirty solution (producing "001" through "015"):

    # Bash 3
    for i in {1000..1015}
    do
      echo "${i:1}"    # or "${i#1}"
    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

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.

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 you are not using Bash, but you have seq(1), and your version of seq(1) lacks printf-style format specifiers. That's a pretty odd set of restrictions, but I suppose it's theoretically possible. Since seq is a nonstandard external tool, it's good to keep your options open.)

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

Some BSD-derived systems have jot(1) instead of seq(1). In accordance with the glorious tradition of Unix, it has a completely incompatible syntax:

   # POSIX shell, OpenBSD et al.
   printf "%02d\n" $(jot 10 1)

   # Bourne shell, OpenBSD (at least)
   jot -w %02d 10 1

Finally, the following example works with any BourneShell derived shell (and where expr and sed are also available) 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/'

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 -v count="$count" 'BEGIN {for (i=1;i<=count;i++) {printf("%03d\n",i)} }'

   # Bourne, with Solaris's decrepit and useless awk:
   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"
      sleep 5
   done

Or, avoiding the subshells (requires bash 3.1):

   # Bash 3.1
   for i in {1..100}; do
      printf -v n %03d $i
      wget "$prefix$n.jpg"
      sleep 5
   done


CategoryShell

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