私はJenkinsからシェルスクリプトを実行しています。これは、Shebangオプションでシェルスクリプトを開始します#!/bin/sh -ex
。
ダミーのBash Shebang? によると、-x
、「シェルに実行トレースを出力させる」。これは、エコーを除いて、ほとんどの目的に最適です。
echo "Message"
出力を生成します
+ echo "Message"
Message
これは少し冗長で、少し奇妙に見えます。離れる方法はありますか-x
有効、ただし出力のみ
Message
上記の2行の代わりに、例えばechoコマンドの前に特別なコマンド文字を付けるか、出力をリダイレクトしますか?
ワニの首まで近づくと、沼を水抜きすることが目的だったことを忘れがちです。—人気のあることわざ
問題は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行)をスクリプトの先頭に挿入し、残りはそのままにしておくことができます。
これにより、
#!/ bin/sh -ex #!/ bin/sh -e #!/ bin/sh –xまたは単純な
#!/ bin/sh期待どおりに動作するはずです。
(シェバン)コマンド1コマンド2コマンド3 set -x コマンド4コマンド5コマンド6 セット+ x コマンド7コマンド8コマンド9そして
echo
でない限り、実行されますがトレースされません。 (ただし、コマンド5がecho
であっても、コマンド6は引き続きトレースされます)。echo
であっても、コマンド9はトレースされません。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
、しかし少なくともそれ以降は静かです。したがって、それは問題の少なくとも部分的な解決策です。
しかし、これを行うより良い方法はありますか? :)
この方法は、set +x
出力を取り除くことにより、独自のソリューションを改善します。
#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "Shell tracing is disabled here"; set -x;
echo "but is enabled here"
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
私は包括的でよく説明された 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) <<<'
すべてのコマンドをグループ化するためにサブシェル((...)
部分)を使用し、入力文字列をstdinに Here String として渡します=(<<<
のこと)はcat -
によって出力されます。 -
はオプションですが、「explicitは暗黙的よりも優れています」と知っています。
echoなしでcat -
を直接使用することもできますが、他の文字列を出力に追加できるため、この方法が好きです。たとえば、私は次のように使用する傾向があります。
BASENAME="$(basename "$0")" # Complete file name
...
echo "[${BASENAME}] $(cat -)"
そして今、それは美しく機能します:
false || echo "Command failed"
> [test.sh] Command failed
実行トレースはstderr
に移動し、次のようにフィルタリングします。
_./script.sh 2> >(grep -v "^+ echo " >&2)
_
ステップごとの説明:
stderr
がリダイレクトされます…– _2>
_>(…)
grep
はコマンドです…^
_+ echo
_…grep
は一致を反転します…– _-v
_stdout
に送られます。属しているstderr
にリダイレクトします。 – _>&2
_問題は(おそらく)この解決策はストリームを非同期化する可能性があります。フィルタリングのため、stderr
はstdout
(デフォルトでは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
を使用して、ストリームを結合する代わりに同期を維持します。
別のアプローチ。 ターミナルで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 >&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
_のおかげで、エコーやその他の出力がないトレースを分析できます。それはあなたが望んでいたものではありませんが、私の分析では、これが(一般的に)正しい方法であると思います。
もちろん、特にstdout
やstderr
をトレースとともに分析する必要がある場合は、別の方法を使用できます。特定の制限と落とし穴があることを覚えておく必要があります。私はそれらのいくつかを見せようとしました。
Makefileでは、@
記号。
使用例:@echo 'message'
this GNU docから:
行が「@」で始まる場合、その行のエコーは抑制されます。 「@」は、行がシェルに渡される前に破棄されます。通常、これは、makefileの進行状況を示すechoコマンドなど、何かを印刷することだけが効果のあるコマンドに使用します。