Bash 变量中的花括号正确扩展,但 rsync 无法正确执行

Bash 变量中的花括号正确扩展,但 rsync 无法正确执行

为什么这些括号在变量中不起作用?

我有一个带有 rsync 的 bash 脚本,带有 --exclude 选项,可调用名为 EXCLUDE 的变量。该变量包括文字花括号。

#!/bin/bash

set -eB

EXCLUDE="{'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'}"

rsync -av --dry-run --exclude=$EXCLUDE web/ ${DEPLOY_USER}@${DEPLOY_HOSTNAME}:/sites/${DEPLOY_DOMAIN}/files/web

如果我回显 $EXCLUDE,我会得到正确的字符串:

echo $EXCLUDE
{'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'}

如果我运行没有 EXCLUDE 变量的脚本,我会得到预期的结果:

rsync -av --dry-run --exclude={'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'} web/ [email protected]:/sites/xyz.org/files/web

building file list ... done
./
test.txt

但是,如果我运行带有 EXCLUDE 变量的命令,结果就不正确:

rsync -av --dry-run --exclude=${EXCLUDE} web/ ${DEPLOY_USER}@${DEPLOY_HOSTNAME}:/sites/${DEPLOY_DOMAIN}/files/web

building file list ... done
./
node_modules
test.log
test.txt

请注意,如果我使用echo !!echo 最后一个运行的命令,结果与没有 $EXCLUDE 变量的版本完全相同:

echo !!
echo rsync -av --dry-run --exclude=$EXCLUDE web/ ${DEPLOY_USER}@${DEPLOY_HOSTNAME}:/sites/${DEPLOY_DOMAIN}/files/web
rsync -av --dry-run --exclude={'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'} web/ [email protected]:/sites/xyz.org/files/web

我甚至可以获取返回的命令echo !!,运行它,并获得预期的结果!

我尝试过将 $EXCLUDE 括在花括号中,尝试过转义变量中的括号,尝试过set -B使用和不使用-e。尽管 bash 似乎确实将花括号发送到 rsync,但 rsync 并未评估括号中的内容。

为什么这些括号在变量中不起作用?

答案1

带有花括号的命令可以工作,因为 shell 对其运行了括号扩展。

--exclude={'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'}

变成

--exclude=.env --exclude=.git* --exclude=*.log --exclude=config/ssh --exclude=/dev --exclude=node_modules --exclude=/web/app/uploads

变量扩展发生得晚于括号扩展,因此将括号存储在变量中不起作用。

你可以使用数组来代替:

excludes=(.env '.git*' '*.log' config/ssh /dev node_modules /web/app/uploads)
rsync -av --dry-run "${excludes[@]/#/--exclude=}" ...

${array[@]/#/PREPEND}语法在参数扩展中描述man bash,它将 PREPEND 添加到数组每个成员的开头。

答案2

据我所知,rsync 本身只能理解--exclude参数中的简单 glob。因此,您依赖交互式 shell 来处理括号扩展。

这意味着{'.env','.git*','*.log','config/ssh','/dev','node_modules','/web/app/uploads'} 不是 “正确的字符串”- 您希望传递--exclude='.env' --exclude='.git*' ...已扩展的 rsync 命令行。

您可以使用括号扩展在数组中,然后在 rsync 命令中展开数组:

EXCLUDE=(--exclude={'.env','.git*','*.log','config/ssh','/dev','node_modules',/web/app/uploads'})

rsync -av --dry-run "${EXCLUDE[@]}" web/ [email protected]:/sites/xyz.org/files/web

也可以看看我们如何运行存储在变量中的命令?

相关内容