私は変数から複雑なコマンドを構成するシェルスクリプトを作業しています。たとえば、次のようになります。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
答え2
eval "$BASH_COMMAND"
コマンドを実行します。
printf '%s\n' "$BASH_COMMAND"
改行文字で指定された正確なコマンドを印刷します。
コマンドに変数が含まれている場合(つまり、などcat "$foo"
)、printコマンドは変数テキストを印刷します。コマンドを実行せずに変数値を印刷することはできませんvariable=$(some_function) other_variable=$variable
。
シェルスクリプトの実行からトレースを取得する最も簡単な方法は、スクリプトを実行するか、xtrace
シェル内で呼び出してシェルオプションを設定することです。トレースは標準エラーで印刷されます。bash -x /path/to/script
set -x