Search code examples
bashloopsfor-loopexpansionbraces

How is the correct way to exapand braces in a "for loop" BASH to positional parameters?


When I try write this function in BASH

function Odds () for i in {1..${#@}..2} ; do echo $i; done

I expected for an output like

1 3 5...

depending on number of arguments passed to the function.

But the efective output is the string with ${#@} expanded.

Ex

{1..5..2}

I have some conjectures but... any away... what is happing and how to avoid this and get the desirable output?


Solution

  • The problem is that brace expansion is done before parameter substitution. Consequently, brace expansion can't be used with variables.

    Option 1: seq

    If you have seq installed (this is common on Linux), try:

    function Odds () { seq 1 2 "${#@}"; }
    

    seq 1 2 "${#@}" returns numbers starting with 1 and incremented by 2 each time until ${#@} is reached.

    For example:

    $ Odds a b c d e
    1
    3
    5
    

    Note: The function notation isn't necessary and limits compatibility (it's not POSIX). Odds can be defined without it:

    Odds () { seq 1 2 "${#@}"; }
    

    Option 2: bash

    Alternatively, just using bash, define:

    function Odds () { for ((i=1; i<=${#@}; i=i+2)); do echo "$i"; done; }
    

    This produces the same output:

    $ Odds a b c d e
    1
    3
    5
    

    Option 3: POSIX

    For widest compatibility, use a function that meets the POSIX standard:

    Odds() { i=1; while [ "$i" -le "$#" ]; do echo "$i"; i=$((i+2)); done; }