评估 $BASH_COMMAND 安全吗?

评估 $BASH_COMMAND 安全吗?

我正在开发一个 shell 脚本,它从变量构造一个复杂的命令,例如像这样(带有我从 Bash FAQ 中学到的技术):

#!/bin/bash

SOME_ARG="abc"
ANOTHER_ARG="def"

some_complex_command \
  ${SOME_ARG:+--do-something "$SOME_ARG"} \
  ${ANOTHER_ARG:+--with "$ANOTHER_ARG"}

如果定义了这些变量,则此脚本会动态添加参数--do-something "$SOME_ARG"和。到目前为止,这工作正常。--with "$ANOTHER_ARG"some_complex_command

但现在我还希望能够在运行命令时打印或记录命令,例如当我的脚本在调试模式下运行时。因此,当我的脚本运行时some_complex_command --do-something abc --with def,我还希望将此命令放在变量中,以便我可以将其记录到系统日志中。

Bash FAQ 演示了一种技术使用DEBUG陷阱和$BASH_COMMAND变量(例如用于调试目的)用于此目的。我已经用以下代码尝试过:

#!/bin/bash

ARG="test string"

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"

echo "Command was: ${COMMAND}"

这有效,但它不会扩展命令中的变量:

host ~ # ./test.sh
test string
Command was: echo "$ARG"

我想我必须使用 eval 来扩展echo "$ARG"echo test string至少我还没有找到没有的方法eval)。以下确实有效:

eval echo "Command was: ${COMMAND}"

它产生以下输出:

host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string

但我不太确定我是否可以eval像这样安全地使用。我尝试利用一些东西但没有成功:

#!/bin/bash

ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER

echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"

它似乎处理得很好,但我很好奇是否有人看到我错过的问题。

答案1

一种可能性是创建一个包装函数,该函数将同时打印命令并执行它,如下所示:

debug() {
    # This function prints (to stdout) its arguments and executes them
    local args=() idx=0 IFS=' ' c
    for c; do printf -v args[idx++] '%q' "$c"; done
    printf "%s\n" "${args[*]}"
    # Execute!
    "$@"
}

这样你就可以在你的脚本中执行以下操作:

debug echo "$ARG"

无需摆弄陷阱。缺点是它debug在代码中添加了一些关键字(但这应该没问题,这样的东西很常见,比如断言等)。

您甚至可以添加全局变量DEBUG并修改debug函数,如下所示:

debug() {
    # This function prints (to stdout) its arguments if DEBUG is non-null
    # and executes them
    if [[ $DEBUG ]]; then
        local args=() idx=0 IFS=' ' c
        for c; do printf -v args[idx++] '%q' "$c"; done
        printf "%s\n" "${args[*]}"
    fi
    # Execute!
    "$@"
}

然后你可以将你的脚本调用为:

$ DEBUG=yes ./myscript

或者

$ DEBUG= ./myscript

要不就

$ ./myscript

取决于您是否想要调试信息。

我将DEBUG变量大写,因为它应该被视为环境变量。DEBUG是一个简单且常见的名称,因此这可能会与其他命令发生冲突。如果您喜欢独角兽,可以称之为“GNIOURF_DEBUGMARTIN_VON_WITTICH_DEBUGUNICORN_DEBUG(然后您可能也喜欢小马)。

笔记。debug函数中,我仔细格式化了每个参数,printf '%q'以便正确转义和引用输出,以便可以通过直接复制和粘贴逐字重用。它还将准确地向您显示 shell 看到的内容,因为您将能够找出每个参数(如果有空格或其他有趣的符号)。该函数还使用 的-vswitch直接赋值printf,以避免不必要的子 shell。

答案2

eval "$BASH_COMMAND"运行命令。

printf '%s\n' "$BASH_COMMAND"打印确切指定的命令以及换行符。

如果命令包含变量(即,如果它类似于cat "$foo"),则打印命令将打印出变量文本。在不执行命令的情况下不可能打印变量的值 - 想想像variable=$(some_function) other_variable=$variable.

从执行 shell 脚本获取跟踪的最简单方法是xtrace通过运行脚本或在 shell 内部bash -x /path/to/script调用来设置 shell 选项。set -x跟踪被打印到标准错误。

相关内容