我的bash脚本包含许多mysqldump blabla > dump.sql
,mysq balbla < dump.sql
以便可以在空运行模式下运行它。
实际上,重点是创建一个函数run
来运行我要求的任何内容。
run echo 'hello world'
run mysqldump blabla > dump.sql
run mysql blabla < dump.sql
run ssh blabla
# etc
run() {
if [[ "$(printenv DRY_RUN)" = "yes" ]]
then
echo "${@}"
else
${@}
fi
}
但是,这是行不通的:
run "mysqldump -uuser -ppass dbase > dump.sql"
我收到此错误:
mysqldump:找不到表:>“
您应该使用
"${@}"
而不是${@}
(如 withecho "${@}"
),但这不是您的问题的原因。原因是重定向发生在命令行解析的早期,在参数替换之前。因此,在变量放入
>
命令行后,shell 不再寻找>
。在我发表答案后我注意到的一个重要点:打电话给
该函数
run
看不到>
和dump.sql
。这可能不是您想要的,因为它会阻止您使用单个环境变量更改所有重定向,因为 的输出echo "${@}"
也会被重定向到文件。因此,您应该使用类似的东西run --redirect dump.sql mysqldump blabla
,见下文。有两种可能:
坚持
"$@"
使用eval
。当然,这可能会让你陷入报价的噩梦。您必须引用除 the 之外的所有内容,>
以便 shell>
在删除引号之前在命令行中看到一个裸字。单独处理重定向:
redirect_target='/dev/stdout'
如果将 放在if [ "$1" == '--redirect' ]
的else
分支中,则可以避免if [[ "$(printenv DRY_RUN)" = "yes" ]]
。就像我最近发布的另一个答案一样,分词发生得太晚而无法启动重定向(但不会太晚而无法影响重定向)。
您可以使用 运行命令
eval
,就像链接帖子中另一个答案中建议的那样,或者使用bash -c "$*"
,同时仍然必须引用整个命令行,包括重定向。尽管如果您想避免引用命令,一种选择是设置
-nv
(noexec
和verbose
),这样 bash 就不会运行命令而只是打印它们。run
因此,在脚本的开头,不要使用 wrapper ,而是执行以下操作:使用
"$@"
(带双引号):没有它们,
$@
扩展为单个令牌。