将 bash 函数包装到 runner 函数中

将 bash 函数包装到 runner 函数中

我的脚本包含许多内容mysqldump blabla > dump.sqlmysq 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,见下文。

有两种可能:

  1. 坚持"$@"并使用eval。当然,这可能会让你陷入一场引用噩梦。您必须引用除 the 之外的所有内容,>以便 shell>在执行引用删除之前在命令行中看到裸露的内容。

  2. 单独处理重定向:

    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' ]中,则可以避免.elseif [[ "$(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(noexecverbose),这样 bash 就不会运行命令,而只是打印它们。因此,run在脚本的开头,不要使用包装器,而是执行以下操作:

[[ $DRY_RUN = yes ]] && set -nv

答案3

使用"$@"(带双引号):

run() {
    if [[ "$(printenv DRY_RUN)" = "yes" ]]
    then
        echo "${@}"
    else
    "$@"
    fi
}

如果没有它们,$@将扩展为单个标记。

相关内容