コマンドが空の文字列を出力するかどうかをテストする


237

コマンドが空の文字列を出力するかどうかをテストするにはどうすればよいですか?


7
コマンドは文字列を返しません(通常、成功の場合は0、失敗の場合は1または2の小さな整数の終了コードを返します)が、文字列を入力するためにデータを出力する場合があります。
Basile Starynkevitch、2012

@BasileStarynkevitchこれは私が必要なものです。コマンドが終了コードを返すことはわかっていますが、コマンドの終了コードは常に0なので、出力を制御する必要があります
barp

1
Bashスクリプトチュートリアルtldp.org/LDP/abs/html
Basile Starynkevitch

Joey Hessにはifneこのためのユーティリティがあります。joeyh.name/code/moreutils
tripleee

回答:


309

以前は、ディレクトリにファイルがあるかどうかを確認する方法についての質問がありました。次のコードはそれを実現しますが、より良いソリューションについてはrspの回答を参照してください。


空の出力

コマンドは値を返さず、出力します。この出力は、コマンド置換を使用してキャプチャできます。例えば$(ls -A)。次のように、Bashで空でない文字列をテストできます。

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

シンボリックの現在の()および親()のディレクトリエントリが省略されているため、-Aではなくを使用したことに注意してください。-a...

注:コメントで指摘されているように、コマンド置換では末尾の改行はキャプチャされません。したがって、コマンドが改行のみを出力する場合、置換は何もキャプチャせず、テストはfalseを返します。非常にまれですが、上記の例ではこれが可能です。単一の改行が有効なファイル名だからです!この回答の詳細情報。


終了コード

コマンドが正常に完了したことを確認する場合$?は、最後のコマンドの終了コード(成功の場合はゼロ、失敗の場合はゼロ以外)を含むを検査できます。例えば:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

詳細はこちら


4
あなたは省略することができ -n、それは単なるなりますので、if [[ $(ls -A) ]]; then
ユーザー

6
このメソッドは、改行のみを出力するコマンドにfalseを与えます。
Ciro Santilli郝海东冠状病六四事件法轮功

89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

私のオリジナルを改善する提案をしてくれたnetjに感謝します
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


これは古い質問ですが、いくつかの改善または少なくともいくつかの説明を必要とする少なくとも2つのことがわかります。

最初の問題

私が目にする最初の問題は、ここで提供される例のほとんどが単に機能しないことです。ls -alおよびls -Alコマンドを使用します-どちらも空のディレクトリに空でない文字列を出力します。これらの例は、ファイルがない場合でも常にファイルがあることを報告します。

そのため、次のように使用する必要がありますls -A- -lとにかく、出力があるかどうかをテストしたいだけなのに、「長いリスト形式を使用する」という意味のスイッチを使用したいのはなぜですか。

したがって、ここでの答えのほとんどは単に正しくありません。

第二の問題

2番目の問題は、一部の回答は問題なく機能するが(使用しないls -alls -Alls -A代わりに)、すべて次のようなことを行うことです。

  1. コマンドを実行する
  2. 出力全体をRAMにバッファリングする
  3. 出力を巨大な単一行の文字列に変換します
  4. その文字列を空の文字列と比較します

私が代わりに行うことをお勧めします:

  1. コマンドを実行する
  2. 保存せずに出力の文字を数える
    • またはさらに良い-最大1文字の数を数えるusing head -c1
      (このアイデアを以下のコメントに投稿してくれたnetjに感謝)
  3. その数をゼロと比較する

たとえば、次の代わりに:

if [[ $(ls -A) ]]

私は使うだろう:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

の代わりに:

if [ -z "$(ls -lA)" ]

私は使うだろう:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

等々。

小さな出力の場合は問題にならないかもしれませんが、大きな出力の場合は差が大きくなる可能性があります。

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

それと比較してください:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

そしてさらに良い:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

完全な例

Will Vousdenの回答の更新例:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

netjによる提案の後に再度更新:

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

jakeonfireによる追加の更新:

grep一致しない場合は失敗して終了します。これを利用して、構文を少し単純化できます。

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

空白を破棄する

テストしているコマンドが空の文字列として扱いたい空白を出力する可能性がある場合は、次の代わりに:

| wc -c

あなたは使うことができます:

| tr -d ' \n\r\t ' | wc -c

またはhead -c1

| tr -d ' \n\r\t ' | head -c1 | wc -c

またはそのようなもの。

概要

  1. まず、動作するコマンドを使用します

  2. 第2に、RAMへの不要な保存と、潜在的に巨大なデータの処理を避けます。

答えは、出力が常に小さいことを指定していなかったため、大きな出力の可能性も考慮する必要があります。


3
[[ $(... | head -c | wc -c) -gt 0 ]]または、コマンドの出力全体を消費する必要[[ -n $(... | head -c1) ]]があるため、より優れていwc -cます。
netj

@netjこれはとても良い考えです。私の更新された答えを見てください-私はあなたの提案を追加しました。どうもありがとう!
rsp

出力を把握する方法に関するアイデアはありますか?
fahrradflucht

一般的なケースではオリジナル(ヘッドなし)の方が良いと思います。コマンドが出力の書き込みを開始したらすぐにコマンドを強制終了することは、常に良い考えとは限りません。
stk

@stkほとんどのプログラムは、killシグナルを受け取った後に安全に終了します。
派手な

40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

これによりコマンドが実行され、返された出力(文字列)の長さがゼロかどうかが確認されます。他のフラグについては、「テスト」のマニュアルページを確認してください。

チェックされる引数の前後に ""を使用します。それ以外の場合、空の結果は、指定された(チェックする)2番目の引数がないため構文エラーになります!

注:これはls -la常に戻るため...それを使用しても機能しません。lsのマニュアルページを参照してください。さらに、これは便利で簡単に思えるかもしれませんが、簡単に壊れてしまうと思います。結果に応じて0または1を返す小さなスクリプト/アプリケーションを書くと、はるかに信頼性が高くなります。


ネストの方が良い$(ので、バッククォートの代わりに使用することをお$(
勧めし

ここにネストする必要はありませんが、常にそれらを使用することをお勧めします。
Veger 2012

23

エレガントでbashのバージョンに依存しないソリューションが必要な場合(実際には他の最新のシェルで動作するはずです)と、ワンライナーを使用して迅速なタスクを行うのが好きな人のために。さあ行こう!

ls | grep . && echo 'files found' || echo 'files not found'

(言及されたコメントの1つとして注意してください、ls -alそして実際、ちょうど-lそして-aすべてが何かを返すので、私の答えでは私は単純なls


5
grep -q ^代わりに使用すると、改行文字も一致し、grepは標準出力に何も出力しません。さらに、入力ストリームが終了するのを待つのではなく、入力を受信するとすぐに終了します。
mortehu

始まる必要がありls -Aますが、ドットファイル(またはディレクトリ)を含めたい場合は
wrlee

13

Bashリファレンスマニュアル

6.4 Bash条件式

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

省略形を使用できます。

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

1
括弧[-...]に-zまたは-nがありません。
71GA 2015年

4
@ 71GA:おそらく見落としやすいかもしれませんが、注意深く見れば、それstringはの同義語であることがわかります-n string
ユーザー

10

Jon Linがコメントしたように、ls -al常に出力されます(.および..)。ls -Alこれらの2つのディレクトリは避けたいものです。

たとえば、コマンドの出力をシェル変数に入れることができます。

v=$(ls -Al)

古い、ネストできない、表記は

v=`ls -Al`

しかし、私はネスト可能な表記を好み$(ます...)

その変数が空でないかどうかをテストできます

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

そして、両方を組み合わせることができます if [ -n "$(ls -Al)" ]; then


5

ls -alコマンドの出力が必要だと思うので、bashでは次のようになります。

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

これは機能しませんls。コマンドが実行されることはありません。変数展開がLS空でないかどうかを確認するだけです。
gniourf_gniourf

1
@gniourf_gniourf-どうしてあなたはそれを考えさせるのですか?バックティックの呪文は$()ほどきれいでも柔軟でもありませんが、動作します...
tink

-aスイッチは、(そこにあるファイルであるかどうか、すなわち、それはコンテンツを返します)は常に暗黙のがあるので、何の内容を返すことはありません.し、..ディレクトリの存在。
wrlee

3

より極端な場合の解決策を次に示します。

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

これは動作します

  • すべてのボーンシェル。
  • コマンド出力がすべてゼロの場合。
  • 出力サイズに関係なく効率的に。

しかしながら、

  • コマンドまたはそのサブプロセスは、何かが出力されると強制終了されます。

1

これまでの回答はすべて、空でない文字列終了して出力するコマンドを扱っています。

ほとんどは次の意味で壊れています:

  • それらは改行だけを出力するコマンドを適切に扱いません。
  • Bash≥4.4以降では、コマンドがnullバイトを出力する場合(コマンド置換を使用するため)、ほとんどが標準エラーをスパムします。
  • ほとんどは完全な出力ストリームを丸呑みするので、応答する前にコマンドが終了するまで待機します。一部のコマンドは終了しません(たとえば、を試してくださいyes)。

これらの問題をすべて修正し、次の質問に効率的に回答するには、

コマンドが空の文字列を出力するかどうかをテストするにはどうすればよいですか?

あなたは使うことができます:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

read-tオプションを使用して、特定の時間内にコマンドが空でない文字列を出力するかどうかをテストするために、タイムアウトを追加することもできます。たとえば、2.5秒のタイムアウトの場合:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

リマーク。コマンドが空でない文字列を出力するかどうかを判断する必要があると思われる場合は、XYの問題が発生している可能性があります。


この答えはあまり評価されていないと思います。私はここで3つの入力シナリオごとに100回の反復を使用して、溶液の一部をパフォーマンステストした(seq 1 10000000seq 1 20、およびprintf"")。この読み取りアプローチが成功しなかった唯一のマッチアップ-z "$(command)"は、空の入力に対するアプローチでした。それでも、約10〜15%しか失われません。周りでも壊れそうですseq 1 10。いずれにせよ、入力サイズが大きく-z "$(command)"なるにつれてアプローチが非常に遅くなるため、可変サイズの入力には使用しないでください。
abathur

0

次に、いくつかのコマンドのstd-outおよびstd-errを一時ファイルに書き込み、そのファイルが空かどうかを確認する別の方法を示します。このアプローチの利点は、両方の出力をキャプチャし、サブシェルやパイプを使用しないことです。これらの後者の側面は、bash出口処理のトラップを妨害する可能性があるため重要です(例:ここ

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"

0

空でない場合は、出力を保存して別のコマンドに渡すこともできます。もしそうなら、あなたは次のようなものを使うことができます

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

このように、rmリストが空の場合、コマンドはハングしません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.