为什么这些括号在变量中不起作用?
我有一个带有 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
也可以看看我们如何运行存储在变量中的命令?