这与我的 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
效果非常好。