スクリプトにデバッグオプションを追加しようとしています。通常、警告などの出力を非表示にしたいので、>/dev/null 2>&1
コマンドをたくさん入れます。
スクリプトをデバッグするには、これらのスクリプトを手動で削除するか、各コマンドをif ... fi
test変数に入れる必要があります$DEBUG
。
>/dev/null 2>&1
変数($REDIR
)を入れて文章を書くとcommand arg1 $REDIR
効果があるようです。デバッグしたい場合は、単に空にしてください$REDIR
。
しかし、私のシェルで簡単なテストを行った結果、そのように動作しないことがわかりました。
~$ echo "bla" >/dev/null 2>&1
~$ REDIR=>/dev/null 2>&1
~$ echo "bla" $REDIR
bla
~$
明らかな理由から、"
or'
の周りを使うことはうまく>/dev/null 2>&1
いきません。
それでは、なぜ私のアイデアはここで機能しないのですか?命令などを変数に入れて呼び出すことを私が間違って理解したのでしょうか?
答え1
リダイレクトはコマンドではないため、この方法では実行できません。を使用すると完了できますが、eval
問題が発生します。
必要なタスクを実行するより良い方法は、デバッグ出力用の関数を持つことです。
function debugprint {
if [ ! -z "$debug" ]; then
echo "$1"
fi
}
debugprint "$(echo 'bla' 2>&1)"
これは、標準エラーを標準出力にリダイレクトし、出力を引数として使用してdebugprint
呼び出されます。今、デバッグをしたいときにやるべきことは、$debug
null以外のものに設定するだけです。
もちろん、これは(引用に関連する)ワームの(他の)缶を開くこともできる。ただ使用したい場合がありますがset -x
、これはデバッグ要件に十分であるかもしれませんし、十分ではないかもしれません。
答え2
この目的のために、私は通常次の関数を定義しますrun
。ほとんどの場合、これはスペースを含むパラメータと他のパラメータを正しく処理します。
#!/bin/bash
run() {
if $DEBUG; then
v=$(exec 2>&1 && set -x && set -- "$@")
echo "#${v#*--}"
"$@"
else
"$@" >/dev/null 2>&1
fi
}
DEBUG=false
run echo "bla"
DEBUG=true
run echo "bla"
run printf "%s . %s . %s\n" bla "more bla" bla
出力:
$ bash debug.sh
# echo bla
bla
# printf '%s . %s . %s\n' bla 'more bla' bla
bla . more bla . bla
答え3
あなたの場合、echoは$REDIR
文字列パラメータとして扱われます。あなたは次のようなものが欲しい:
~$ echo "bla" >/dev/null 2>&1
~$ REDIR='>/dev/null 2>&1'
~$ eval "echo bla $REDIR"
~$
しかし、高速で汚れたハッキングを試みない限り、Wouter Verhelstはより良いソリューションを提供します(実際にはそれほど長くても複雑ではありません)。
答え4
私は私が書くすべてのシェル関数に対して同じことをします。
fn(){
echo some normal stderr debug stuff >&2 #if $DBG 2>stderr
dd if="\$DBG/please/report/on/this/file" #ditto
echo I DEFINITELY need to handle this >&3 #always stderr
( PATH=; ".some" oops I expect to handle ) 2>&4 #always /dev/null
echo and the regular stuff #always unaffected
} 4<>/dev/null 3>&2 2>&"$((${#DBG}?3:4))"
私はいくつかの理由でこれが好きです。
lenを使用した
${#DBG}
評価は常に保証されています。>=0テストの整数値 -$DBG
実際に含まれるすべての値。DBG=IFS=0
たとえば、これは数学を安全にします。関数が実行されるたびに、実際の操作は一度だけ実行されます
open()
。それ/dev/null
以外は。/dev/null
#fd>&4
デバッグ出力(有効にすることができます
set -x
)は、デフォルトでインタラクティブシェルに関数をダンプします。〜しない限り環境変数を$DBG
null以外の値に明示的に設定しました。- 空でない場合、リダイレクトされた
$DBG
数学的拡張はそれ自体を指し、これはと評価されます2>&3
。 - ただし、それ以外は公開記述子として評価されるため、
2>&4
公開/dev/null
記述子に移動します。 - 通常、識別して保存した関数の20行実行トレースを表示する必要はありませんが、
~/.sh/fn/...
識別した場合は、関連するコマンドラインはまったく異なる可能性がありますset -x
。 $DBG
null/not setの場合にも便利ですがset -x
はい$PS4
stderrには移動しませんが、#fd>&3
まだ到達可能なコマンド固有の評価出口を開くために有効になります。
- 空でない場合、リダイレクトされた
合理的な方法で継承を実装します。
- 他の関数を呼び出す関数は、
$DBG
どのような方法でもこの値に影響を与えることはできません。したがって、すでに書き込めない場合は、デフォルトでstderrへの書き込みを開始できます。 - 設定されていない、またはnullの場合、
$DBG
呼び出すすべてのサブ関数は、呼び出さない限り#fd3
Evenの意味を失いますchild_fn 2>&3
。この場合、親関数が明示的にstderrに書き込むのと同じ機会を得ます。 - それできる設定しても、
$DBG
これらのサブ機能は静かに設定されません。$DBG
- そしてそれはできる置く
$DBG
これにより、最上位関数を呼び出すことができます。後ろにデフォルトの stderr 出力がイネーブルになります。
- 他の関数を呼び出す関数は、
私はstderrのコピーを保持しているので、
#fd>&3
関数が必要な場合はその記述子からstderrに明示的に書き込むことができます。(上記の場合は除く)。記述子は関数をラップする複合コマンドにのみ接続されているため、fds 2、3、4の現在のシェル値に影響を与えずに自分で閉じます。
- クリーニング
{3,4}>&-
は必要ありません。
- クリーニング