我的巴什脚本包含许多内容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:找不到表:“>”
答案1
您应该使用"${@}"
而不是${@}
(如 with echo "${@}"
),但这不是您问题的原因。
原因是重定向发生在命令行解析的早期,在参数替换之前。因此,在将变量放入>
命令行后,shell 不再查找>
。
在发布我的答案后,我注意到了一个重要的点:通过像这样的电话
run mysqldump blabla > dump.sql
该函数run
看不到>
和dump.sql
。这可能不是您想要的,因为它阻止您使用单个环境变量更改所有重定向,因为 的输出echo "${@}"
也会重定向到文件。因此,您应该使用类似的东西run --redirect dump.sql mysqldump blabla
,见下文。
有两种可能:
坚持
"$@"
并使用eval
。当然,这可能会让你陷入一场引用噩梦。您必须引用除 the 之外的所有内容,>
以便 shell>
在执行引用删除之前在命令行中看到裸露的内容。单独处理重定向:
run --redirect dump.sql mysqldump blabla run() { if [ "$1" == '--redirect' ]; then shift redirect_target="$1" shift else redirect_target='/dev/stdout' # works at least with Linux fi if [[ "$(printenv DRY_RUN)" = "yes" ]] then echo "${@}" else "${@}" > "$redirect_target" fi }
redirect_target='/dev/stdout'
如果将 放在的分支if [ "$1" == '--redirect' ]
中,则可以避免.else
if [[ "$(printenv DRY_RUN)" = "yes" ]]
if [[ "$(printenv DRY_RUN)" = "yes" ]] then if [ "$1" == '--redirect' ]; then # do not output "--redirect file" shift shift fi echo "${@}" else if [ "$1" == '--redirect' ]; then shift redirect_target="$1" shift "${@}" > "$redirect_target" else "${@}" fi fi
答案2
像我最近发布的另一个答案,分词发生得太晚,无法启动重定向(但还不算太晚,无法影响重定向)。
您可以使用 运行命令eval
,就像链接帖子中另一个答案中建议的那样,或者使用bash -c "$*"
,同时仍然必须引用整个命令行(包括重定向)。
不过,如果您想避免引用命令,一种选择是设置-nv
(noexec
和verbose
),这样 bash 就不会运行命令,而只是打印它们。因此,run
在脚本的开头,不要使用包装器,而是执行以下操作:
[[ $DRY_RUN = yes ]] && set -nv
答案3
使用"$@"
(带双引号):
run() {
if [[ "$(printenv DRY_RUN)" = "yes" ]]
then
echo "${@}"
else
"$@"
fi
}
如果没有它们,$@
将扩展为单个标记。