bash - 在 bash 变量中引用不受尊重

标签 bash scripting variables quotes

我将命令的参数存储在一个变量中。我想要的最终命令是:

mock -r myconfig --define "debug_package %{nil}" --resultdir results --rebuild mypackage.src.rpm

这是我的尝试:

set -x    # for debugging

RESULTDIR=results
MOCK_CONFIG="myconfig"
MOCK_ARGS="-r $MOCK_CONFIG --define \"debug_package %{nil}\" --resultdir $RESULTDIR"
cmd="mock $MOCK_ARGS --rebuild mypackage.src.rpm"
$cmd

结果是:

+ RESULTDIR=results
+ MOCK_CONFIG=myconfig
+ MOCK_ARGS='-r myconfig --define "debug_package %{nil}" --resultdir results'
+ cmd='mock -r myconfig --define "debug_package %{nil}" --resultdir results --rebuild mypackage.src.rpm'
+ mock -r myconfig --define '"debug_package' '%{nil}"' --resultdir results --rebuild mypackage.src.rpm
ERROR: Bad option for '--define' ("debug_package).  Use --define 'macro expr'

如您所见,--define 参数的参数没有被正确引用。 --define 认为我只传递了 debug_package,这是不完整的。

在定义 MOCK_ARGS 时,我尝试了引号的各种变体,甚至试图转义 debug_package%{nil} 之间的空格。

什么引号和/或转义符的组合允许我构建这个参数列表并从这个脚本执行命令?

编辑:

我将生成的命令存储在一个变量中的原因是因为它最终被传递给一个函数,该函数执行一些日志记录,然后执行命令。

另外,我遇​​到了this FAQ这表明我应该使用数组而不是变量。我已经开始尝试使用数组,但到目前为止还没有有效的解决方案。

最佳答案

数组是解决此类问题的方法。这是我想出的:

log_and_run() {
    echo "$(date): running command: $*"
    "$@"
    echo "$1 completed with status $?"
}

RESULTDIR=results
MOCK_CONFIG="myconfig"
MOCK_ARGS=(-r "$MOCK_CONFIG" --define "debug_package %{nil}" --resultdir "$RESULTDIR")
cmd=(mock "${MOCK_ARGS[@]}" --rebuild mypackage.src.rpm)
log_and_run "${cmd[@]}"
# could also use: log_and_run mock "${MOCK_ARGS[@]}" --rebuild mypackage.src.rpm

请注意 $* 扩展为所有由空格分隔的参数(适合传递给日志命令);而“$@”将所有参数扩展为单独的单词(如在 log_and_run 中使用的那样,第一个将被视为要执行的命令,其余的将被视为它的参数);类似地,“${MOCK_ARGS[@]}”扩展为 MOCK_ARGS 的所有元素作为单独的单词,无论它们是否包含空格(因此 MOCK_ARGS 的每个元素都成为 cmd 的元素,没有引号解析混淆)。

此外,我还引用了 MOCK_CONFIG 和 RESULTDIR 的扩展;这在这里不是必需的,因为它们不包含空格,但这是一个好习惯,以防它们碰巧包含空格(例如,它是从外部传递到您的脚本中的吗?那么您应该假设它可能包含空格) .

顺便说一句,如果您需要向函数传递额外的参数(我将使用要登录的文件名示例),您可以使用 array slicing仅拆分后面的参数以用作记录/执行的命令:

log_and_run() {
    echo "$(date): running command ${*:2}" >>"$1" 
    "${@:2}"
    echo "$2 completed with status $?" >>"$1"
}
#...
log_and_run test.log "${cmd[@]}"

附录:如果你想在日志中引用包含空格的参数,你可以“手动”构造日志字符串并做任何你想引用的事情。例如:

log_and_run() {
    local log_cmd=""
    for arg in "$@"; do
        if [[ "$arg" == *" "* || -z "$arg" ]]; then
            log_cmd="$log_cmd \"$arg\""
        else
            log_cmd="$log_cmd $arg"
        fi
    done
    echo "$(date): running command:$log_cmd"
    ...

请注意,这将处理参数中的空格和空白参数,但不处理(例如)参数中的双引号。 “正确”执行此操作可能会变得任意复杂...

另外,我构造 log_cmd 字符串的方式,它以一个前导空格结尾;我在上面通过在 echo 命令中省略它之前的空格来处理这个问题。如果您需要实际修剪空间,请使用 "${log_cmd# }"

关于bash - 在 bash 变量中引用不受尊重,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58612754/

相关文章:

bash |在另一台计算机上运行脚本时出现语法错误?

javascript - Dynamics CRM -- 在表单脚本中处理 onLoad 与 OnSave 事件

c# - 我应该在自己的线程中运行每个插件吗?

php - 在 SQL WHERE 语句中使用 session 变量

php - 如何获取字符串中某个单词前后的所有内容

javascript - 当我将 grep 与 Node child_process 一起使用时,结果与 bash 中不同

git - 克隆的 github 存储库上的文件和目录权限

c# - RayCastHit 找不到错误消息 *Unity*

python - 为什么在全局范围而不是封闭范围中查找未初始化的类变量?

regex - 使用带有否定前瞻的 grep 不返回任何匹配项