Danny Lin Danny Lin - 3 months ago 9
Bash Question

How to pass variable number of parameters to a shell command?

I am writing a script for backup like this:

backup.sh:

dir="$1"
mode="$2"
delta="$3"

for file in "$dir/backup."*".$mode.tar.gz"; do
[ "$file" -nt "$ref" ] && ref="$file"
done

if [ "$delta" = "true" ]; then
delta_cmd=-N "'$ref'"
fi

backup_file="$dir/backup.$(date +%Y%m%d-%H%M%S).$mode.tar.gz"

case "$mode" in
config)
tar -cpzvf "$backup_file" $delta_cmd \
/etc \
/usr/local
;;
# still other modes here...
esac


I want to pass a single variable
$delta_cmd
to the tar command so that it tars all files or only delta files since last backup depending on the value of
$delta
.

The above code creates an error message and does not tar the delta files correctly if
$delta
is set to true. How to fix it?

P.S: The script would better be POSIX compatible.

Answer

As a POSIX-compliant approach, consider:

set --                  # clear $@
if [ -f "$ref" ]; then
  set -- "$@" -N "$ref" # add -N "$ref" to $@
fi

tar ... "$@" ...        # expand $@ into command line

To put this all in context, and protect the main argument list against overwrite, might look like:

#!/bin/sh

main() {
    # if current shell supports "local", prevent variables from leaking
    # ...some "POSIX" shells, such as ash, will be fine with this.
    local dir mode delta target_file backup_file 2>&1 ||:

    dir=$1
    mode=$2
    delta=$3

    set -- # clear $@

    for file in "$dir/backup."*".$mode.tar.gz"; do
        [ "$file" -nt "$ref" ] && ref="$file"
    done

    if [ "$delta" = "true" ]; then
        set -- "$@" -N "$ref"
    fi

    target_file="$dir/backup.$(date +%Y%m%d-%H%M%S).$mode.tar.gz"

    case "$mode" in
        config)
            tar -cpzvf "$target_file" "$@" \
                /etc \
                /usr/local
            ;;
        # still other modes here...
    esac
}

main "$@"
Comments