评估$ BASH_COMMAND是否安全?


11

我正在研究一个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,我也希望将此命令包含在变量中,以便例如将其记录到syslog中。

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}"

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


这就是我多年来在sudo包装器中使用过的东西,但我从未注意到有问题。
w00t 2014年

Answers:


7

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

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在整个代码中添加一些关键字(但这应该没问题,通常有诸如asserts之类的东西)。

您甚至可以添加全局变量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_DEBUG或者MARTIN_VON_WITTICH_DEBUG或者UNICORN_DEBUG如果你喜欢独角兽(然后你可能喜欢小马太)。

注意。debug函数中,我仔细地对每个参数进行了格式化,以printf '%q'使输出可以正确地转义和引用,以便可以直接复制和粘贴地重复使用。它还将向您确切显示外壳程序看到的内容,因为您可以弄清楚每个参数(在空格或其他有趣符号的情况下)。此功能还使用的-v开关直接分配,printf以避免不必要的子外壳。


1
该函数很好用,但是不幸的是我的命令包含重定向和“后台执行”控制操作符&。我不得不将它们移到函数上才能使其正常工作-不太好,但是我认为没有更好的方法。但是没有更多了eval,所以我已经准备好了,这很不错:)
Martin von Wittich 2014年

5

eval "$BASH_COMMAND" 运行命令。

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

如果命令包含变量(例如cat "$foo"),则打印出命令会打印出变量文本。不执行命令就无法打印变量的值-想想类似的命令variable=$(some_function) other_variable=$variable

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


1
我知道xtrace,但这并不能给我太多控制权。我尝试了“ set -x; ...; set + x”,但是:1)也显示了禁用xtrace的“ set + x”命令2)我无法为输出加上前缀,例如带有时间戳3)我可以请勿将其记录到syslog。
Martin von Wittich

2
“不执行命令就不可能打印变量的值”-很好的例子,我没有考虑过这种情况。如果bash旁边有一个BASH_COMMAND包含扩展命令的附加变量,那就很好了,因为在执行命令的某个时刻,它必须在命令上进行变量扩展:)
Martin von Wittich 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.