build argument lists containing whitespace

2019-01-09 05:02发布

问题:

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?

回答1:

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
    }
    


回答2:

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[@]}"


回答3:

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



回答4:

You can use the arrays just as you suggest, with a small detail changed. The line calling foo should read

 foo "${args[@]}"


回答5:

I had a problem with this too as well. I was writing a bash script to backup the important files on my windows computer (cygwin). I tried the array approach too, and still had some issues. Not sure exactly how I fixed it, but here's the parts of my code that are important in case it will help you.

WORK="d:\Work Documents\*"
#   prompt and 7zip each file
for x in $SVN $WEB1 $WEB2 "$WORK" $GRAPHICS $W_SQL
do
    echo "Add $x to archive? (y/n)"
    read DO
    if [ "$DO" == "y" ]; then
        echo "compressing $x"
        7zip a $W_OUTPUT "$x"
    fi
    echo ""
done