build argument lists containing whitespace

In bash one can escape arguments that contain whitespace.

foo "a string"

This also works for arguments to a command or function:

bar() {
    foo "$@"
}

bar "a string"

So far so good, but what if I want to manipulate the arguments before calling foo?

This does not work:

bar() {
    for arg in "$@"
    do
        args="$args \"prefix $arg\""
    done

    # Everything looks good ...
    echo $args

    # ... but it isn't.
    foo $args

    # foo "$args" would just be silly
}

bar a b c

So how do you build argument lists when the arguments contain whitespace?


There are (at least) two ways to do this:

  1. Use an array and expand it using "${array[@]}":

    bar() {
        local i=0 args=()
        for arg in "$@"
        do
            args[$i]="prefix $arg"
            ((++i))
        done
    
        foo "${args[@]}"
    }
    

    So, what have we learned? "${array[@]}" is to ${array[*]} what "$@" is to $*.

  2. Or if you do not want to use arrays you need to use eval:

    bar() {
        local args=()
        for arg in "$@"
        do
            args="$args \"prefix $arg\""
        done
    
        eval foo $args
    }
    

Here is a shorter version which does not require the use of a numeric index:

(example: building arguments to a find command)

dir=$1
shift
for f in "$@" ; do
    args+=(-iname "*$f*")
done
find "$dir" "${args[@]}"

Use arrays (one of the hidden features in Bash).