使用存储在字符串中的参数调用 rsync

使用存储在字符串中的参数调用 rsync

这与我的 mhddfs 线程中引用的脚本相同,我想根据一些输入定义参数,然后调用 rasync。我目前想到了这个,但只有对 rsync 的调用不起作用:

#!/bin/bash

INCLUDE=""
EXCLUDE=""
USER=""
HOST=""
KEY=""
FORCE=false
SHUTDOWN=false
DESTINATION=""
LASTFULL=""

while getopts u:h:k:s:o:f:e:i: option
do
    case "${option}" in
        u) USER=${OPTARG};;
        i) INCLUDE=${OPTARG};;
        e) EXCLUDE=${OPTARG};;
        h) HOST=${OPTARG};;
        k) KEY=${OPTARG};;
        s) SHUTDOWN=true;;
        o) DESTINATION=${OPTARG};;
        f) FORCE=true;
    esac
done

if [ -z $USER ] || [ -z $HOST ] || [ -z $DESTINATION ]
then
    echo "Usage : $0 <-u : user> <-h : host> <-t : type> <-o : output directory> [-k : key] [-s : shutdown]"
    exit 1
fi

if [ ! -d "$DESTINATION/../Backup" ]
then
    if [ ! -d "$DESTINATION/Backup" ]
    then
        echo "Creating backup directory on target..."

        if ! mkdir "$DESTINATION/Backup"
        then
            echo "Could not create the backup directory. Exiting."
            exit 2
        fi
    fi
    DESTINATION="$DESTINATION/Backup"
fi

RSYNC_ARGS="-ravh -H --rsync-path=\"sudo rsync\" --append --progress --delete --safe-links"
if [ -f "$KEY" ]
then
    RSYNC_ARGS="$RSYNC_ARGS -e \"ssh -i $KEY\""
fi
if [ -f "$INCLUDE" ]
then
    echo "Including only files from $INCLUDE"
    RSYNC_ARGS="$RSYNC_ARGS --files-from=$INCLUDE"
fi
if [ -f "$EXCLUDE" ]
then
    echo "Excluding files from $EXCLUDE"
    RSYNC_ARGS="$RSYNC_ARGS --exclude-from=$EXCLUDE"
fi
if ! $FORCE
then
    LASTBACKUP=$(basename $(ls -td  $DESTINATION/*/ | sed "y/\t/\n/" | head -n 1 ) 2>/dev/null)
    echo "Processing backup based on --> $LASTBACKUP"
    RSYNC_ARGS="$RSYNC_ARGS --link-dest=../$LASTBACKUP"
else
    echo "Processing full backup"
fi

DESTINATION=$(echo "$DESTINATION/$(date '+%Y_%m_%d')" | sed "s#//#/#g")

rsync $RSYNC_ARGS $USER@$HOST:/ $DESTINATION 2> error.log

CODE=$?
if [ $CODE -eq 0 ]
then
    echo "Backup done : $DESTINATION"
    exit 0
else
    echo "Rsync stopped with code $CODE"
    exit $CODE
fi

我明白了

Rsync stopped with code 1

error.log 包含

Unexpected remote arg : <correct username>@<correct host>:/

当我回应 rsync 调用并复制粘贴它时,它起作用了

我知道 $RSYNC_ARGS 搞乱了这一切,但我不知道如何正确调用它。有什么想法吗?

答案1

shell 在扩展变量之前会解析引号,因此将引号放在变量的值中并不会达到预期的效果 —— 等到它们到位时,已经太晚了,无法做任何有用的事情。请参阅BashFAQ #50:我尝试将命令放入变量中,但复杂的情况总是失败!更多细节。

在这种情况下,如果您想要动态构建命令的参数列表,最好的选择是将参数列表存储在数组中。这样,在创建数组时会解析引号,每个“单词”都会存储为单独的数组元素,如果您正确引用变量,数组元素就会包含在命令的参数列表中,而无需进行任何不必要的解析:

rsync_args=(-ravh -H --rsync-path="sudo rsync" --append --progress --delete --safe-links)
if [ -f "$key" ]
then
    rsync_args+=(-e "ssh -i $key")
fi
if [ -f "$include" ]
then
    echo "Including only files from $include"
    rsync_args+=(--files-from="$include")
fi
# ... etc
rsync "${rsync_args[@]}" "$user@$host:/" "$destination" 2> error.log

+=请注意,赋值中的所有括号和;以及[@]使用数组时的双引号、大括号和必需的以使其正常工作。

顺便说一句,你可能注意到我把所有变量名都改成了小写。在 shell 中使用大写变量名很危险,因为其中许多变量名对 shell 和/或其他程序具有特殊含义,不小心重复使用其中一个变量名可能会产生意想不到的效果。事实上,$USER$HOST就是这样的(它们被初始化为当前本地用户名和主机名);重复使用它们大概不会造成麻烦,但最好使用小写或混合大小写的变量名来避免此问题。但您可能希望user=$USER在脚本开头添加,这样如果-u没有指定,它将默认使用相同的用户名。

另外,我为没有双引号的变量引用添加了双引号。通常情况下,不用双引号也无妨,但最好还是使用双引号,以免出现潜在问题。我建议通过以下方式运行脚本:shellcheck.net,因为它擅长发现类似这样的常见问题。

答案2

直到我上次发帖后 40 分钟,我才能够发帖。在此期间,我使用了

eval rsync $RSYNC_ARGS $USER@$HOST:/ $DESTINATION 2> error.log

效果非常好。

相关内容