出力リダイレクトを動的に使用するには?

出力リダイレクトを動的に使用するには?

スクリプトにデバッグオプションを追加しようとしています。通常、警告などの出力を非表示にしたいので、>/dev/null 2>&1コマンドをたくさん入れます。

スクリプトをデバッグするには、これらのスクリプトを手動で削除するか、各コマンドをif ... fitest変数に入れる必要があります$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呼び出されます。今、デバッグをしたいときにやるべきことは、$debugnull以外のものに設定するだけです。

もちろん、これは(引用に関連する)ワームの(他の)缶を開くこともできる。ただ使用したい場合がありますが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))"

私はいくつかの理由でこれが好きです。

  1. lenを使用した${#DBG}評価は常に保証されています。>=0テストの整数値 -$DBG実際に含まれるすべての値。DBG=IFS=0たとえば、これは数学を安全にします。

  2. 関数が実行されるたびに、実際の操作は一度だけ実行されますopen()。それ/dev/null以外は。/dev/null#fd>&4

  3. デバッグ出力(有効にすることができますset -x)は、デフォルトでインタラクティブシェルに関数をダンプします。〜しない限り環境変数を$DBGnull以外の値に明示的に設定しました。

    • 空でない場合、リダイレクトされた$DBG数学的拡張はそれ自体を指し、これはと評価されます2>&3
    • ただし、それ以外は公開記述子として評価されるため、2>&4公開/dev/null記述子に移動します。
    • 通常、識別して保存した関数の20行実行トレースを表示する必要はありませんが、~/.sh/fn/...識別した場合は、関連するコマンドラインはまったく異なる可能性がありますset -x
    • $DBGnull/not setの場合にも便利ですがset -x はい$PS4stderrには移動しませんが、#fd>&3まだ到達可能なコマンド固有の評価出口を開くために有効になります。
  4. 合理的な方法で継承を実装します。

    • 他の関数を呼び出す関数は、$DBGどのような方法でもこの値に影響を与えることはできません。したがって、すでに書き込めない場合は、デフォルトでstderrへの書き込みを開始できます。
    • 設定されていない、またはnullの場合、$DBG呼び出すすべてのサブ関数は、呼び出さない限り#fd3Evenの意味を失いますchild_fn 2>&3。この場合、親関数が明示的にstderrに書き込むのと同じ機会を得ます。
    • それできる設定しても、$DBGこれらのサブ機能は静かに設定されません。$DBG
    • そしてそれはできる置く $DBGこれにより、最上位関数を呼び出すことができます。後ろにデフォルトの stderr 出力がイネーブルになります。
  5. 私はstderrのコピーを保持しているので、#fd>&3関数が必要な場合はその記述子からstderrに明示的に書き込むことができます。(上記の場合は除く)

  6. 記述子は関数をラップする複合コマンドにのみ接続されているため、fds 2、3、4の現在のシェル値に影響を与えずに自分で閉じます。

    • クリーニング{3,4}>&-は必要ありません。

関連情報