web-dev-qa-db-ja.com

エコーコマンドの実行トレースを抑制しますか?

私はJenkinsからシェルスクリプトを実行しています。これは、Shebangオプションでシェルスクリプトを開始します#!/bin/sh -ex

ダミーのBash Shebang? によると、-x、「シェルに実行トレースを出力させる」。これは、エコーを除いて、ほとんどの目的に最適です。

echo "Message"

出力を生成します

+ echo "Message"
Message

これは少し冗長で、少し奇妙に見えます。離れる方法はありますか-x有効、ただし出力のみ

Message

上記の2行の代わりに、例えばechoコマンドの前に特別なコマンド文字を付けるか、出力をリダイレクトしますか?

29
Christian

ワニの首まで近づくと、沼を水抜きすることが目的だったことを忘れがちです。—人気のあることわざ

問題はechoについてですが、これまでの回答の大部分は、_set +x_コマンドをこっそりと取り込む方法に焦点を当ててきました。より単純で直接的な解決策があります:

_{ echo "Message"; } 2> /dev/null
_

(以前の回答で見ていなかった場合、_{ …; } 2> /dev/null_について考えていなかった可能性があることを認めます。)

これはやや面倒ですが、連続するechoコマンドのブロックがある場合、各コマンドに対して個別に実行する必要はありません。

_{
  echo "The quick brown fox"
  echo "jumps over the lazy dog."
} 2> /dev/null
_

改行がある場合はセミコロンは不要です。

kenorbのアイデア を使用して_/dev/null_を永続的に非標準のファイル記述子(3など)で永続的に開き、_2>&3_の代わりに_2> /dev/null_と発声することで、入力の負担を軽減できます。 )__いつも。


この記事の執筆時点での最初の4つの回答では、echoを実行するときに特別な(ほとんどの場合、面倒な)everyを実行する必要があります。allechoコマンドで実行トレースを抑制したい場合(そして、なぜそうしないのでしょうか?)、大量のコードを変更することなく、グローバルに抑制できます。まず、エイリアスが追跡されていないことに気づきました。

_$ myfunc()
> {
>     date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016  0:00:00 AM           # Happy Halloween!
$ myfunc
+ myfunc                                # Note that function call is traced.
+ date
Mon, Oct 31, 2016  0:00:01 AM
$ myalias
+ date                                  # Note that it doesn’t say  + myalias
Mon, Oct 31, 2016  0:00:02 AM
_

(Shebangが_#!/bin/sh_の場合、_/bin/sh_がbashへのリンクであっても、次のスクリプトスニペットは機能します。ただし、Shebangが_#!/bin/bash_の場合は、_shopt -s expand_aliases_コマンドを使用して、スクリプトで機能するエイリアスを取得します。

だから、私の最初のトリックについて:

_alias echo='{ set +x; } 2> /dev/null; builtin echo'
_

ここで、_echo "Message"_と言うと、トレースされないエイリアスを呼び出しています。エイリアスは、トレースオプションをオフにし、setコマンドからのトレースメッセージを抑制し(最初に ser5071535の回答 で示された手法を使用)、実際のechoコマンドを実行します。これにより、everyechoコマンドでコードを編集する必要なく、user5071535の回答と同様の効果を得ることができます。ただし、これによりトレースモードがオフのままになります。エイリアスはWordの代わりに文字列のみを許可するため、_set -x_をエイリアスに含めることはできません(少なくとも簡単にはできません)。コマンドにエイリアス文字列の一部を挿入することはできませんafter引数(例:_"Message"_)。したがって、たとえば、スクリプトに

_date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
_

出力は

_+ date
Mon, Oct 31, 2016  0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016  0:00:04 AM           # Note that it doesn’t say  + date
_

したがって、メッセージを表示した後でもトレースオプションをオンに戻す必要があります。ただし、連続するechoコマンドのblockごとに1回だけです。

_date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
_


いいですねechoの後に_set -x_を自動化できるとしたら、もう少し巧妙にできます。しかし、それを紹介する前に、これを考慮してください。 OPは_#!/bin/sh -ex_シバンを使用するスクリプトから始まります。暗黙的に、ユーザーはxをシバンから削除し、実行トレースなしで正常に機能するスクリプトを作成できます。その特性を維持するソリューションを開発できたらいいですね。ここでの最初のいくつかの回答は、echoステートメントの後でトレースがオンになっているかどうかに関係なく無条件にトレースを「オンに戻す」ため、このプロパティは失敗します。 この答え は、その問題の認識に著しく失敗しています。それは、replacesechoの出力とトレース出力です。したがって、トレースをオフにすると、すべてのメッセージが消えます。ここで、echoステートメントconditionallyの後にトレースをオンに戻すソリューションを提示します(既にオンになっている場合のみ)。これを無条件にトレースを「戻す」ソリューションにダウングレードすることは簡単であり、練習として残しておきます。

_alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
        builtin echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}
_

_$-_はオプションリストです。設定されているすべてのオプションに対応する文字の連結。たとえば、eおよびxオプションが設定されている場合、_$-_は、eおよびxを含む文字の寄せ集めになります。新しいエイリアス(上記)は、トレースをオフにする前に_$-_の値を保存します。次に、トレースをオフにすると、シェル関数に制御が渡されます。この関数は実際のechoを実行し、エイリアスが呼び出されたときにxオプションがオンになっていたかどうかを確認します。オプションがオンだった場合、関数はそれをオンに戻します。オフの場合、関数はオフのままにします。

上記の7行(shoptを含めると8行)をスクリプトの先頭に挿入し、残りはそのままにしておくことができます。

これにより、

  1. 次のシバン行のいずれかを使用する:
    #!/ bin/sh -ex 
    #!/ bin/sh -e 
    #!/ bin/sh –x
    または単純な
    #!/ bin/sh
    期待どおりに動作するはずです。
  2. のようなコードを持つ
    (シェバン)コマンド1コマンド2コマンド
     set -x 
    コマンド4コマンド5コマンド6
    セット+ x 
    コマンド7コマンド8コマンド9
    そして
    • コマンド4、5、6はトレースされます—それらの1つがechoでない限り、実行されますがトレースされません。 (ただし、コマンド5がechoであっても、コマンド6は引き続きトレースされます)。
    • コマンド7、8、および9はトレースされません。コマンド8がechoであっても、コマンド9はトレースされません。
    • コマンド1、2、および3は、Shebangにxが含まれているかどうかに応じて(4、5、6など)トレースされるか、またはトレースされません(7、8、9など)。

追伸私のシステムでは、真ん中の回答(builtinの単なるエイリアスになっているもの)でechoキーワードを省略できることがわかりました。これは驚くべきことではありません。 bash(1)は、エイリアスの展開中に、…

…展開されているエイリアスと同一の単語は、2回目に展開されません。つまり、たとえばls_ls -F_にエイリアスすることができ、bashは置換テキストを再帰的に展開しようとしません。

当然のことながら、builtinキーワードが省略されている場合、最後の回答(_echo_and_restore_を含むもの)は失敗します1。しかし、奇妙なことに、builtinを削除して順序を入れ替えると機能します。

_echo_and_restore() {
        echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
_

__________
1未定義の動作を引き起こすようです。私は見た

  • 無限ループ(おそらく無限の再帰が原因)
  • _/dev/null: Bad address_エラーメッセージ、および
  • コアダンプ。

InformIT で部分的な解決策が見つかりました:

#!/bin/bash -ex
set +x; 
echo "Shell tracing is disabled here"; set -x;
echo "but is enabled here"

出力

set +x; 
Shell tracing is disabled here 
+ echo "but is enabled here"
but is enabled here

残念ながら、それでもset +x、しかし少なくともそれ以降は静かです。したがって、それは問題の少なくとも部分的な解決策です。

しかし、これを行うより良い方法はありますか? :)

12
Christian

この方法は、set +x出力を取り除くことにより、独自のソリューションを改善します。

#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "Shell tracing is disabled here"; set -x;
echo "but is enabled here"
2
user5071535

set +x括弧内にあるため、ローカルスコープにのみ適用されます。

例えば:

#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true

出力:

$ ./foo.sh 
+ exec
foo1
foo2
foo3
+ true
2
kenorb

私は包括的でよく説明された g-manによる回答 が大好きで、これまでに提供された中で最高のものと考えています。スクリプトのコンテキストを考慮し、不要な構成を強制しません。したがって、この答えを最初に読んでいる場合は、最初にその答えを確認してください。すべてのメリットがあります。

ただし、その答えには重要な欠けている部分があります。提案された方法は、エラーを報告するなどの一般的なユースケースでは機能しません。

COMMAND || echo "Command failed!"

エイリアスの構成方法により、これは次のように拡張されます

COMMAND || { save_flags="$-"; set +x; } 2>/dev/null; echo_and_restore "Command failed!"

そして、あなたが推測したように、echo_and_restoreは無条件にalways実行されます。 set +x部分が実行されなかった場合、その関数の内容も出力されることを意味します。

最後の;&&に変更しても機能しません。これは、Bashでは||&&left-associative であるためです。

この使用例で機能する変更を見つけました:

echo_and_restore() {
    echo "$(cat -)"
    case "$save_flags" in
        (*x*) set -x
    esac
}
alias echo='({ save_flags="$-"; set +x; } 2>/dev/null; echo_and_restore) <<<'

すべてのコマンドをグループ化するためにサブシェル((...)部分)を使用し、入力文字列をstdinHere String として渡します=(<<<のこと)はcat -によって出力されます。 -はオプションですが、「explicitは暗黙的よりも優れています」と知っています。

echoなしでcat -を直接使用することもできますが、他の文字列を出力に追加できるため、この方法が好きです。たとえば、私は次のように使用する傾向があります。

BASENAME="$(basename "$0")"  # Complete file name
...
echo "[${BASENAME}] $(cat -)"

そして今、それは美しく機能します:

false || echo "Command failed"
> [test.sh] Command failed
1
j1elo

実行トレースはstderrに移動し、次のようにフィルタリングします。

_./script.sh 2> >(grep -v "^+ echo " >&2)
_

ステップごとの説明:

  • stderrがリダイレクトされます…– _2>_
  • …コマンドに。 - >(…)
  • grepはコマンドです…
  • …これには行の先頭が必要です…– _^_
  • …後に続く_+ echo_…
  • …その後、grepは一致を反転します…– _-v_
  • …それで、不要な行はすべて破棄されます。
  • 結果は通常stdoutに送られます。属しているstderrにリダイレクトします。 – _>&2_

問題は(おそらく)この解決策はストリームを非同期化する可能性があります。フィルタリングのため、stderrstdout(デフォルトではecho出力が属します)に比べて少し遅れる場合があります。これを修正するには、stdoutで両方を使用してもかまわない場合は、最初にストリームに参加できます。

_./script.sh > >(grep -v "^+ echo ") 2>&1
_

スクリプト自体にそのようなフィルタリングを組み込むことはできますが、このアプローチは確実に非同期化する傾向があります(つまり、テストで発生しました:コマンドの実行トレースは、直後のechoの出力の後に表示される場合があります) 。

コードは次のようになります。

_#!/bin/bash -x

{
 # original script here
 # …
} 2> >(grep -v "^+ echo " >&2)
_

トリックなしで実行:

_./script.sh
_

この場合も、> >(grep -v "^+ echo ") 2>&1を使用して、ストリームを結合する代わりに同期を維持します。


別のアプローチ。 ターミナルでstdoutstderrが混在しているため、「少し冗長」で奇妙な出力が得られます。これら2つのストリームは、理由により異なる動物です。 stderrの分析がニーズにのみ合うかどうかを確認してください。 stdoutを破棄:

_./script.sh > /dev/null
_

スクリプトにechostderrへのデバッグ/エラーメッセージの出力がある場合、上記の方法で冗長性を取り除くことができます。完全なコマンド:

_./script.sh > /dev/null 2> >(grep -v "^+ echo " >&2)
_

今回はstderrのみを使用しているため、非同期化はもはや問題ではありません。残念ながら、この方法では、echo(存在する場合)に出力されるstdoutのトレースも出力も表示されません。フィルターを再構築してリダイレクト(_>&2_)を検出することもできますが、_echo foobar >&2_、_echo >&2 foobar_、および_echo "foobar >&2"_を見ると、状況が複雑になることにおそらく同意します。

多くは、スクリプトにあるエコーに依存します。複雑なフィルターを実装する前によく考えてください。逆効果になる可能性があります。いくつかの重要な情報を誤って見落とすよりも、少し冗長性を持たせるほうがよいでしょう。


echoの実行トレースを破棄する代わりに、その出力と、トレースを除くすべての出力を破棄できます。実行トレースのみを分析するには、次のことを試してください。

_./script.sh > /dev/null 2> >(grep "^+ " >&2)
_

フールプルーフ?いいえ。スクリプトに_echo "+ rm -rf --no-preserve-root /" >&2_があるとどうなるか考えてください。誰かが心臓発作を起こすかもしれません。


そして最後に…

幸い、_BASH_XTRACEFD_環境変数があります。 _man bash_から:

BASH_XTRACEFD
有効なファイル記述子に対応する整数に設定されている場合、bashは_set -x_が有効になっているときに生成されたトレース出力をそのファイル記述子に書き込みます。

次のように使用できます。

_(exec 3>trace.txt; BASH_XTRACEFD=3 ./script.sh)
less trace.txt
_

最初の行がサブシェルを生成することに注意してください。この方法では、ファイル記述子は有効なままではなく、後で現在のシェルで割り当てられた変数もありません。

_BASH_XTRACEFD_のおかげで、エコーやその他の出力がないトレースを分析できます。それはあなたが望んでいたものではありませんが、私の分析では、これが(一般的に)正しい方法であると思います。

もちろん、特にstdoutstderrをトレースとともに分析する必要がある場合は、別の方法を使用できます。特定の制限と落とし穴があることを覚えておく必要があります。私はそれらのいくつかを見せようとしました。

0

Makefileでは、@記号。

使用例:@echo 'message'

this GNU docから:

行が「@」で始まる場合、その行のエコーは抑制されます。 「@」は、行がシェルに渡される前に破棄されます。通常、これは、makefileの進行状況を示すechoコマンドなど、何かを印刷することだけが効果のあるコマンドに使用します。

0
derek