$BASH_COMMANDを評価するのは安全ですか?

$BASH_COMMANDを評価するのは安全ですか?

私は変数から複雑なコマンドを構成するシェルスクリプトを作業しています。たとえば、次のようになります。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

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_DEBUGまたはMARTIN_VON_WITTICH_DEBUG」と呼んでくださいUNICORN_DEBUG(それでポニーも好きかもしれません)。

メモ。debug関数内でprintf '%q'出力が適切にエスケープされ、引用されるように各引数の型を慎重に指定し、直接コピーして貼り付けることでそのまま再利用できるようにしました。また、空白やその他の興味深い記号がある場合は、各引数を見つけることができるので、シェルが表示する内容を正確に表示します。この機能は、不要なサブシェルを回避するために-vスイッチを直接割り当てる方法も使用します。printf

おすすめ記事