How do I iterate over a range of numbers defined by variables in Bash?

后端 未结 20 1868
予麋鹿
予麋鹿 2020-11-21 05:19

How do I iterate over a range of numbers in Bash when the range is given by a variable?

I know I can do this (called \"sequence expression\" in the Bash documentatio

相关标签:
20条回答
  • 2020-11-21 05:44

    These are all nice but seq is supposedly deprecated and most only work with numeric ranges.

    If you enclose your for loop in double quotes, the start and end variables will be dereferenced when you echo the string, and you can ship the string right back to BASH for execution. $i needs to be escaped with \'s so it is NOT evaluated before being sent to the subshell.

    RANGE_START=a
    RANGE_END=z
    echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash
    

    This output can also be assigned to a variable:

    VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`
    

    The only "overhead" this should generate should be the second instance of bash so it should be suitable for intensive operations.

    0 讨论(0)
  • 2020-11-21 05:47

    If you're on BSD / OS X you can use jot instead of seq:

    for i in $(jot $END); do echo $i; done
    
    0 讨论(0)
  • 2020-11-21 05:48
    for i in $(seq 1 $END); do echo $i; done

    edit: I prefer seq over the other methods because I can actually remember it ;)

    0 讨论(0)
  • 2020-11-21 05:48

    discussion

    Using seq is fine, as Jiaaro suggested. Pax Diablo suggested a Bash loop to avoid calling a subprocess, with the additional advantage of being more memory friendly if $END is too large. Zathrus spotted a typical bug in the loop implementation, and also hinted that since i is a text variable, continuous conversions to-and-fro numbers are performed with an associated slow-down.

    integer arithmetic

    This is an improved version of the Bash loop:

    typeset -i i END
    let END=5 i=1
    while ((i<=END)); do
        echo $i
        …
        let i++
    done
    

    If the only thing that we want is the echo, then we could write echo $((i++)).

    ephemient taught me something: Bash allows for ((expr;expr;expr)) constructs. Since I've never read the whole man page for Bash (like I've done with the Korn shell (ksh) man page, and that was a long time ago), I missed that.

    So,

    typeset -i i END # Let's be explicit
    for ((i=1;i<=END;++i)); do echo $i; done
    

    seems to be the most memory-efficient way (it won't be necessary to allocate memory to consume seq's output, which could be a problem if END is very large), although probably not the “fastest”.

    the initial question

    eschercycle noted that the {a..b} Bash notation works only with literals; true, accordingly to the Bash manual. One can overcome this obstacle with a single (internal) fork() without an exec() (as is the case with calling seq, which being another image requires a fork+exec):

    for i in $(eval echo "{1..$END}"); do
    

    Both eval and echo are Bash builtins, but a fork() is required for the command substitution (the $(…) construct).

    0 讨论(0)
  • 2020-11-21 05:49

    You can use

    for i in $(seq $END); do echo $i; done
    
    0 讨论(0)
  • 2020-11-21 05:49

    This works in Bash and Korn, also can go from higher to lower numbers. Probably not fastest or prettiest but works well enough. Handles negatives too.

    function num_range {
       # Return a range of whole numbers from beginning value to ending value.
       # >>> num_range start end
       # start: Whole number to start with.
       # end: Whole number to end with.
       typeset s e v
       s=${1}
       e=${2}
       if (( ${e} >= ${s} )); then
          v=${s}
          while (( ${v} <= ${e} )); do
             echo ${v}
             ((v=v+1))
          done
       elif (( ${e} < ${s} )); then
          v=${s}
          while (( ${v} >= ${e} )); do
             echo ${v}
             ((v=v-1))
          done
       fi
    }
    
    function test_num_range {
       num_range 1 3 | egrep "1|2|3" | assert_lc 3
       num_range 1 3 | head -1 | assert_eq 1
       num_range -1 1 | head -1 | assert_eq "-1"
       num_range 3 1 | egrep "1|2|3" | assert_lc 3
       num_range 3 1 | head -1 | assert_eq 3
       num_range 1 -1 | tail -1 | assert_eq "-1"
    }
    
    0 讨论(0)
提交回复
热议问题