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


29

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

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

echo "Message"

出力を生成します

+ echo "Message"
Message

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

Message

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

回答:


20

ワニの首に近づいたら、沼地を空にすることが目標だったことを忘れがちです。                   —人気のあることわざ

問題はについてですが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

改行がある場合、セミコロンは必要ないことに注意してください。

非標準のファイル記述子(例:3)で永続的に 開き、常に発言する代わりに発言するというkenorbのアイデアを使用することで、入力の負担を軽減でき/dev/nullます。2>&32> /dev/null


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

$ 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

(次のスクリプトスニペットは#!/bin/sh/bin/shbashへのリンクであっても、shebangがの場合に機能することに注意してください。しかし、shebangがの場合、#!/bin/bash追加する必要がありますshopt -s expand_aliasesスクリプトでエイリアスを取得コマンドをます。

だから、私の最初のトリック:

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

さて、私たちが言うときecho "Message"、私たちはトレースされないエイリアスを呼び出しています。エイリアスはトレースオプションをオフにし、setコマンドからのトレースメッセージを抑制し(user5071535の回答で最初に提示された手法を使用)、実際のechoコマンドを実行します。これにより echoコマンドごとにコードを編集する必要なく、user5071535の回答と同様の効果を得ることができます。ただし、これによりトレースモードはオフのままになります。set -xエイリアスでは単語を文字列に置き換えることしかできないため、エイリアスにa を入れることはできません(少なくとも簡単にはできません)。引数のにコマンドエイリアス文字列の一部を挿入することはできません (例:)"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

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

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


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

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
}

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

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

これにより、

  1. 次のシバン行のいずれかを使用するには:
    #!/ bin / sh -ex
    #!/ bin / sh -e
    #!/ bin / sh –x
    または単なるプレーン
    #!/ bin / sh
    期待どおりに動作するはずです。
  2. のようなコードを持っている
    (シバン)
    コマンド1
    コマンド2
    コマンド3
    -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は、シバンにが含まれているかどうかに応じて(4、5、6など)またはトレースされません(7、8、9など)x

PS私は、私のシステムでbuiltinは、中間回答(キーワードの単なるエイリアスecho)のキーワードを省略できることを発見しました。これは驚くことではありません。bash(1)は、エイリアスの展開中、…

…展開されているエイリアスと同一の単語は、再度展開されません。これは、たとえばにエイリアスlsする可能性がls -Fあり、bashは置換テキストを再帰的に展開しようとしないことを意味します。

当然のことながら、キーワードが省略されたecho_and_restore場合、最後の答え(を持つもの)は失敗します1。しかし、奇妙なことに、それを削除して順序を切り替えると機能します:builtinbuiltin

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エラーメッセージ、および
  • コアダンプ。

2
エイリアスを使用して驚くべき手品をいくつか見たので、その知識が不完全であることを知っています。誰かがecho +x; echo "$*"; echo -xエイリアスで同等のことをする方法を提示できるなら、私はそれを見たいです。
G-Manが「Reinstate Monica」と言う

11

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しますが、少なくともその後は静かです。したがって、それは問題の少なくとも部分的な解決策です。

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


2

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

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

2

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

私が間違っている場合は修正してください。ただしset +x、サブシェルの内部(またはサブシェルの使用)が何か役に立つとは思いません。削除して同じ結果を得ることができます。一時的にトレースを「無効にする」作業を行っているのは、stderrを/ dev / nullにリダイレクトすることです... echo foo1 2>/dev/nullなどは、同様に効果的で読みやすいようです。
タイラーリック

スクリプトにトレースがあると、パフォーマンスに影響する可能性があります。第二に、&2をNULLにリダイレクトすることは、他のエラーが予想される場合と同じではない可能性があります。
ケノーブ

私が間違っている場合は修正してくださいが、あなたの例では、スクリプトでトレースが有効になっており(with bash -x)、すでに&2nullにリダイレクトしています(null &3にリダイレクトされているため)ので、そのコメントがどのように関連しているのかわかりません。あなたのポイントを説明するより良い例が必要なのかもしれませんが、少なくともこの例では、利益を失うことなく単純化できるようです。
タイラーリック

1

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

ただし、その答えには重要な部分が欠落しています。提案された方法は、典型的なユースケース、つまりエラーの報告では機能しません。

COMMAND || echo "Command failed!"

エイリアスの構築方法により、これは次のように展開されます

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

そして、あなたはそれを推測し、無条件に常にecho_and_restore実行されます。ことを考える部分が実行されなかった、それはその関数の内容があまりにも、印刷されますことを意味します。set +x

最後に変更;することが&&あるためバッシュで、いずれかの動作しない、||&&されている左結合

このユースケースで機能する変更を見つけました。

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) <<<'

(...)すべてのコマンドをグループ化するためにサブシェル(パーツ)を使用してから、入力文字列を標準文字列としてHere String<<<事)として渡しますcat --オプションですが、あなたは知っている、「明示的、暗黙よりも優れています」。

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

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

そして今、それは美しく動作します:

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

0

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

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

いくつかの説明、ステップバイステップ:

  • stderr リダイレクトされます…– 2>
  • …コマンドへ。–>(…)
  • grep コマンドは…
  • …行の先頭が必要です…– ^
  • …後に続く+ echo
  • …その後grep、試合を反転します…–-v
  • …そして、それはあなたが望まないすべての行を破棄します。
  • 結果は通常に行きstdoutます; stderrそれが属する場所にリダイレクトします。–>&2

問題は、このソリューションがストリームを非同期化する可能性があることだと思います。フィルタリングのため、(出力がデフォルトで属する場合)stderrに関して少し遅れる場合があります。それを修正するために、両方に入れることを気にしない場合は、最初にストリームに参加できます:stdoutechostdout

./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ますが、ストリームを結合するコストで同期を維持するために使用します。


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

./script.sh > /dev/null

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

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

今回は作業stderrのみを行っているため、非同期化はもはや問題ではありません。残念ながら、この方法では、トレースは表示されず、echoその出力はstdout(存在する場合)出力されません。私たちは(リダイレクトを検出するために、私たちのフィルタを再構築しようとすることができます>&2)がありますが、見ればecho foobar >&2echo >&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'

この GNUドキュメントから:

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


こんにちは、参照しているドキュメントはシェルではなくgnu make用です。動作しますか?エラー./test.sh: line 1: @echo: command not foundが表示されますが、bashを使用しています。

すみません、質問を完全に読み間違えました。はい、@メイクファイルにエコーしている場合にのみ機能します。
デレク

@derek元の返信を編集したので、ソリューションがMakefileのみに限定されていることが明確に示されました。私は実際にこれを探していたので、あなたのコメントが否定的な評判を持たないようにしたかったです。うまくいけば、人々もそれが役立つと思うでしょう。
シャカロン

-1

司会者ごとに2016年10月29日に編集され、オリジナルには何が起こっているのかを理解するのに十分な情報が含まれていなかったという提案があります。

この「トリック」は、xtraceがアクティブなときに端末で1行のメッセージ出力を生成します。

元の質問は-xを有効のままにする方法はありますが、2行ではなくMessageのみを出力することです。これは質問からの正確な引用です。

「set -xを有効のままにして、メッセージの1行を生成する方法」という質問を理解していますか?

  • グローバルな意味では、この質問は基本的に美学に関するものです-質問者は、xtraceがアクティブである間に生成される2つの実質的に重複する行の代わりに単一の行生成することを望みます。

要約すると、OPには以下が必要です。

  1. 持っている-xセット効果に
  2. 人間が読めるメッセージを作成する
  3. メッセージ出力を1行だけ生成します。

OPでは、echoコマンドを使用する必要ありませ。彼らは、としてそれを引用略語使用して、メッセージの生成の「例えば」ラテンexempliグレーシア、またはを表します。

「ボックスの外側」を考え、エコーを使用してメッセージを生成することを放棄すると、割り当てステートメントはすべての要件を満たすことができることに注意してください。

テキストメッセージ(メッセージを含む)を非アクティブな変数に割り当てます。

この行をスクリプトに追加してもロジックは変更されませんが、トレースがアクティブなときに1行が生成されます(変数$ echoを使用している場合は、名前を別の未使用の変数に変更するだけです)

echo="====================== Divider line ================="

端末に表示されるのは1行のみです。

++ echo='====================== Divider line ================='

OPは嫌いなので、2つではありません。

+ echo '====================== Divider line ================='
====================== Divider line =================

デモするサンプルスクリプトを次に示します。メッセージへの変数置換(末尾から4行目の$ HOMEディレクトリ名)が機能するため、このメソッドを使用して変数をトレースできることに注意してください。

#!/bin/bash -exu
#
#  Example Script showing how, with trace active, messages can be produced
#  without producing two virtually duplicate line on the terminal as echo does.
#
dummy="====================== Entering Test Script ================="
if [[ $PWD == $HOME ]];  then
  dummy="*** "
  dummy="*** Working in home directory!"
  dummy="*** "
  ls -la *.c || :
else
  dummy="---- C Files in current directory"
  ls -la *.c || :
  dummy="----. C Files in Home directory "$HOME
  ls -la  $HOME/*.c || :
fi

そして、ルートで実行し、次にホームディレクトリで実行した場合の出力を次に示します。

$ cd /&&DemoScript
+ dummy='====================== Entering Test Script ================='
+ [[ / == /g/GNU-GCC/home/user ]]
+ dummy='---- C Files in current directory'
+ ls -la '*.c'
ls: *.c: No such file or directory
+ :
+ dummy='----. C Files in Home directory /g/GNU-GCC/home/user'
+ ls -la /g/GNU-GCC/home/user/HelloWorld.c /g/GNU-GCC/home/user/hw.c
-rw-r--r-- 1 user Administrators 73 Oct 10 22:21 /g/GNU-GCC/home/user/HelloWorld.c
-rw-r--r-- 1 user Administrators 73 Oct 10 22:21 /g/GNU-GCC/home/user/hw.c
+ dummy=---------------------------------

$ cd ~&&DemoScript
+ dummy='====================== Entering Test Script ================='
+ [[ /g/GNU-GCC/home/user == /g/GNU-GCC/home/user ]]
+ dummy='*** '
+ dummy='*** Working in home directory!'
+ dummy='*** '
+ ls -la HelloWorld.c hw.c
-rw-r--r-- 1 user Administrators 73 Oct 10 22:21 HelloWorld.c
-rw-r--r-- 1 user Administrators 73 Oct 10 22:21 hw.c
+ dummy=---------------------------------

1
削除された回答の編集バージョン(これはコピーです)でさえ、OPが求めていた関連するechoコマンドなしでメッセージだけを出力するわけではないため、質問にはまだ回答していません。
DavidPostill

注意してください、この回答はメタで議論されています
DavidPostill

@Davidメタフォーラムのコメントごとに、説明を拡大しました。
-HiTechHiTouch

@David再び、ラテン語の「.eg」に注意して、OPを非常に注意深く読むことをお勧めします。ポスターには、echoコマンドを使用する必要はありません。OPは、問題を解決するための潜在的な方法の例として(リダイレクトとともに)エコーのみを参照します。どちらも必要ないことが判明
-HiTechHiTouch

そして、OPが次のようなことをしたい場合はどうなりecho "Here are the files in this directory:" *ますか?
G-Manが「Reinstate Monica」と言う
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.