Skip to main content

How do I iterate over a range of numbers in bash?


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:




for i in {1..5}; do echo $i; done



Which gives:




1

2

3

4

5




Yet how can I replace either of the range endpoints with a variable? This doesn't work:




END=5
for i in {1..$END}; do echo $i; done



Which prints:




{1..5}



Source: Tips4allCCNA FINAL EXAM

Comments

  1. for i in `seq 1 $END`; do echo $i; done

    ReplyDelete
  2. discussion

    Using seq is fine, as Jim Robert 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 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 cleanest way, and possibly the "fastest"; it sure won't be necessary to allocate memory to consume seq's output, which could be a problem if END is very large.

    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).

    ReplyDelete
  3. The seq method is the simplest, but Bash has built-in arithmetic evaluation.

    END=5
    for ((i=1;i<=END;i++)); do
    echo $i
    done
    # ==> outputs 1 2 3 4 5 on separate lines


    The "for ((expr1;expr2;expr2))" construct works just like "for (expr1;expr2;expr3)" in C and similar languages, and like other ((expr)) cases, Bash treats them as arithmetic.

    ReplyDelete
  4. You can use

    for i in $(seq $END); do echo $i; done

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

    for i in $(jot $END); do echo $i; done

    ReplyDelete
  6. This works for me in bash:

    END=5
    i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
    done

    ReplyDelete
  7. Since the "how to" part of the question has been completely answered by now, I will comment on why the original expression didn't work.

    From man bash:


    Brace expansion is performed before
    any other expansions, and any
    characters special to other
    expansions are preserved in the
    result. It is strictly textual. Bash
    does not apply any syntactic
    interpretation to the context of
    the expansion or the text between the
    braces.


    So, brace expansion is something done early as a purely textual macro operation, before parameter expansion.

    Shells are highly optimized hybrids between macro processors and more formal programming languages. In order to optimize the typical use cases, the language is made rather more complex and some limitations are accepted.

    ReplyDelete
  8. Another layer of indirection:

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

    ReplyDelete
  9. My version of bash doesn't seem to support the curly brace notation at all.

    Can you do this?

    for i in `echo {1..$END}`; do echo $i; done


    Update: I found a bash that supports the curly braces; the above does not work.

    ReplyDelete
  10. 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.

    ReplyDelete
  11. You would still use seq in cases where you needed to generate sequences of real numbers. The
    Bash for loop only supports integers.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex