【问题标题】:Issues quoting exclusions for rsync in bash script [duplicate]在 bash 脚本中引用 rsync 排除项的问题 [重复]
【发布时间】:2016-12-23 13:40:13
【问题描述】:

我正在使用 bash 脚本通过 rsync 同步 Web 文件夹。我正在使用要排除的文件夹和文件数组,并且在为排除列表转义这些项目时遇到问题...

我的排除列表是这样定义的......

SYNC_EXCLUSIONS=(
    '/exclude_folder_1'
    '/exclude_folder_2'
    '.git*'
    '.svn'
)

然后我像这样构建我的排除字符串......

exclusions='';
for e in "${SYNC_EXCLUSIONS[@]}"
do
    exclusions+=" --exclude='$e'";
done

最后我执行了我的 rsync...

rsync --recursive --delete $exclusions "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"

如果我回显该命令,它看起来很完美,如果我在提示符下复制并执行它,它可以正常工作。但是,当从脚本运行时,排除项将被忽略。

我发现如果我从每个排除项目周围删除单引号,它会起作用,就像这样......

exclusions+=" --exclude=$e";

我不希望这样做,以防我需要排除带有空格或特殊字符的文件夹。

有什么方法可以让脚本在保留排除项目周围的引号的同时使其工作?我尝试了各种引号和反斜杠等的组合,但我尝试过的都没有。

【问题讨论】:

  • 请看BashFAQ #50
  • 顺便说一句,为您自己的变量使用全大写名称是不好的形式。请参阅POSIX guidelines on environment variable names(第 4 段),指定 shell 和系统使用全大写名称的变量,应用程序可以使用其他名称的变量而不会发生冲突;因为 shell 变量共享一个命名空间(使用环境变量的名称设置 shell 变量会覆盖后者),这些约定也适用于 shell 变量。
  • 谢谢,我不知道这一点。这是我在其他各种语言中使用的约定,表示某些东西是不变的。我没有意识到这不是 bash 的好习惯。我将更改我的代码。
  • 公平地说,这是一个没有像应有的那样经常遵循的约定——但它黑字 POSIX,可以让人们远离麻烦 (比如无意中运行了for PATH in */; do ...,并拥有一个无法再执行外部程序的解释器。

标签: bash rsync


【解决方案1】:

您根本无法为此构建一个字符串——请参阅BashFAQ #50 了解有关原因的详细讨论。构建一个数组。

exclusions=( )
for e in "${SYNC_EXCLUSIONS[@]}"; do
    exclusions+=( --exclude="$e" )
done

rsync --recursive --delete "${exclusions[@]}" "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"

...好吧,根本无法构建字符串,除非您要使用eval 执行它。但是,以不易出现 shell 注入漏洞的方式执行此操作需要小心:

printf -v exclusions_str '--exclude=%q ' "${SYNC_EXCLUSIONS[@]}"
printf -v rsync_cmd 'rsync --recursive --delete %s %q %q' \
  "$exclusions_str" "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"
eval "$rsync_cmd"

【讨论】:

  • 谢谢,效果很好。
  • 您可以通过字符串替换的魔力创建exclusions 数组而无需循环:exclusions=("${SYNC_EXCLUSIONS[@]/#/--exclude=}")。这扩展了SYNC_EXCLUSIONS,但在每个元素中,将字符串(“#”)的开头“替换”为“--exclude="。