On Sat, Sep 20, 2025 at 21:25:44 +0200, pourko--- via Bug reports for the GNU 
Bourne Again SHell wrote:
> > Also, the provided seq function shadows /usr/bin/seq
> > [...] The patch to fix these issues...
> 
> Shadowing a command is not really an "issue".
> 
> In this example, a function shadows an external command,
> hence it is in the examples dir.

Honestly, both the "repeat" and "seq" functions provided in
the examples/startup-files directory are broken in ways that would
disqualify them from use, for me.

The "seq" function looks like this:

# Subfunction needed by `repeat'.
seq ()
{ 
    local lower upper output;
    lower=$1 upper=$2;

    if [ $lower -ge $upper ]; then return; fi
    while [ $lower -le $upper ];
    do
        echo -n "$lower "
        lower=$(($lower + 1))
    done
    echo "$lower"
}

I have two -- no, three -- no, four -- issues with this function:

 1) It's overriding the seq(1) command on systems which have one, with
    a less capable alternative.

 2) It's "off by one" if you provide two sensible inputs.  This is the
    issue that I believe prompted this entire thread.

        hobbit:~$ s 1 3
        1 2 3 4

 3) If the inputs are equal, then based on the above, you would expect
    it to give two numbers as output.  But because of the "if" check,
    it gives none.

        hobbit:~$ s 1 1
        hobbit:~$ 

 4) It uses echo -n instead of printf.  echo is not reliable.

The "repeat" function looks like this:

# "repeat" command.  Like:
#
#       repeat 10 echo foo
repeat ()
{ 
    local count="$1" i;
    shift;
    for i in $(seq 1 "$count");
    do
        eval "$@";
    done
}

This one "only" has two issues:

 1) It's forking a subshell to generate a list of numbers instead of just
    using builtin arithmetic and "for" or "while" to loop.  (The command
    that it's invoking also gives the wrong number of numbers, so the
    loop loops the wrong number of times.  It's broken two ways, as noted
    above.)

 2) It's using eval "$@".  This is just bad.  It should either be using "$@"
    without eval, which would preserve the command arguments, or it should
    be using eval "$*" which would join the command arguments into a single
    string.

I would replace these two functions with one of the following:

# Execute a simple command N times.
repeat() {
    if (($# < 2)); then
        echo "usage: repeat N command [arg] [...]" >&2
        return 1
    fi

    local n="$1" i
    shift

    for ((i=1; i<=n; i++)); do
        "$@"
    done
}

or

# Execute a command N times.
# If -e is provided, use eval on the command and its arguments.
# Otherwise, execute the command and its arguments exactly as given.
repeat() {
    if (($# < 2)); then
        echo "usage: repeat [-e] N command [arg] [...]" >&2
        return 1
    fi

    local e=0 n i
    if [[ $1 = -e ]]; then
        e=1
        shift
    fi
    n=$1
    shift

    for ((i=1; i<=n; i++)); do
        if ((e)); then
            eval "$*"
        else
            "$@"
        fi
    done
}

The second one lets the user "opt in" to eval's syntax parsing if needed,
without sacrificing the ability to execute commands in the way most
people would expect as a default.

Reply via email to