web-dev-qa-db-ja.com

bashのifテストとコマンドを一緒に使用するにはどうすればよいですか?

クラッシュログのあるディレクトリがあり、findコマンドに基づくbashスクリプトで条件ステートメントを使用したいと思います。

ログファイルは次の形式で保存されます。

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

過去5分間に変更された特定のアプリのクラッシュログが存在する場合にのみ、ifステートメントでtrueを返します。私が使用するfindコマンドは次のとおりです。

find /var/log/crashes -name app-\*\.log -mmin -5

これをifステートメントに適切に組み込む方法がわかりません。私はこれがうまくいくと思います:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

不明な点がいくつかあります。

  • if flags を確認しましたが、どれを使用すればよいかわかりません。
  • testディレクティブが必要ですか、それともfindコマンドの結果に対して直接処理するだけですか、またはfind... | wc -l代わりに行数を取得するには?
  • この質問に答えるのに100%必要ではありませんが、testは、コマンドが返す戻りコードに対してテストするためのものですか?そして、それらは目に見えない-stdout/stderrの外では?私はmanページを読みましたが、testをいつ使用し、どのようにデバッグするかについてはまだはっきりしていません。
26
cwd

_[_とtestは同義語です(ただし、_[_は_]_が必要)を除くため、_[ test_は使用しないでください。

_[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'
_

testは、条件が真の場合はゼロの終了ステータスを返し、そうでない場合はゼロ以外を返します。これを実際に任意のプログラムに置き換えて、終了ステータスを確認できます。0は成功を示し、0以外は失敗を示します。

_# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi
_

ただし、上記の例はすべてプログラムの終了ステータスに対してのみテストし、プログラムの出力は無視します。

findの場合、出力が生成されたかどうかをテストする必要があります。 _-n_空でない文字列かどうかをテストします。

_if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi
_

テスト引数の完全なリストは、bashコマンドラインで_help test_を呼び出すことで利用できます。

bashを使用している場合(shではなく)、_[[ condition ]]_を使用できます。これは、条件にスペースまたはその他の特殊なケースがある場合に、より予測どおりに動作します。それ以外は、通常、_[ condition ]_を使用する場合と同じです。この例では、可能な限り_[[ condition ]]_を使用しています。

また、_`command`_を$(command)に変更しました。これも通常は同じように動作しますが、ネストされたコマンドの方が適しています。

28
mrb

エラーがなければfindは正常に終了するため、その終了ステータスを頼りにファイルが見つかったかどうかを知ることはできません。しかし、言ったように、見つかったファイルの数を数え、その数をテストすることができます。

これは次のようなものです。

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(別名[)はコマンドのエラーコードをチェックしません。テストを実行するための特別な構文があり、テストが成功した場合はエラーコード0で終了します。またはそれ以外の場合は1。渡されたコマンドのエラーコードをチェックし、それに基づいて本体を実行するのはifです。

man test(またはbashを使用している場合はhelp test)とhelp if(同上)を参照してください。

この場合、wc -lは数値を出力します。 testのオプション-gtを使用して、その数が0より大きいかどうかをテストします。ある場合、test(または[)は終了コード0を返します。 ifはその終了コードを成功と解釈し、コードを本体内で実行します。

9
angus

これは

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

または

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

コマンドtest[ … ]はまったく同じ意味です。唯一の違いは、その名前と、[が最後の引数として終了]を必要とするという事実です。いつものように、コマンド置換を二重引用符で囲みます。二重引用符を使用しないと、findコマンドの出力が単語に分割されます。一致するファイルが複数ある場合(およびそこにある場合)、構文エラーが発生します引数がない場合、[ -n ]はtrueですが、[ -n "" ]はfalseです)。

Ksh、bash、zshでは、ashでは使用できませんが、[[ … ]]を使用することもできます。[は通常のコマンドですが、[[ … ]]は別の構文解析構文です。 [[ … ]]内に二重引用符は必要ありません(ただし、害はありません)。コマンドの後に;が必要です。

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

これは潜在的に非効率になる可能性があります。/var/log/crashesに多くのファイルがある場合、findはそれらすべてを調査します。一致が見つかるとすぐに、またはすぐ後にfindを停止する必要があります。 GNU find(非組み込みLinux、Cygwin))では、-quitプライマリを使用します。

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

他のシステムでは、findheadにパイプして、少なくとも最初の一致の直後に終了します(findは壊れたパイプで終了します)。

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

headコマンドでサポートされている場合は、head -c 1を使用できます。)


または、zshを使用します。

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

これはうまくいくはずです

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi
1
Steven Penny
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

ここで適切なソリューションです。

-exec service myapp restart ';'を指定すると、シェルが何かを解釈する必要はなく、直接実行したいコマンドがfindによって呼び出されます。

-quitを指定すると、コマンドの処理後にfindが終了し、基準に一致するファイルが複数ある場合にコマンドが再度実行されるのを防ぎます。

0
Jules