web-dev-qa-db-ja.com

ネストされたパイプのgrepは「(標準入力)」という文字列になります

私はこのようにネストされたgrepを実行しています:

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results"

これは意図したとおりに機能します(2番目のgrepによって最初のgrepからフィルターされた結果も取得します)が、「-l」オプションを追加するとすぐに、2番目のgrepからのみファイルのリストを取得します。何も取得しません。

grep -ir "Some string" . |grep -l "Another string I want to find in the other grep's results"

これにより、次の出力が得られます。

(標準出力)

ファイルのリストだけが必要な場合、パイプは機能しないと思います。代替案はありますか?

6
OMA

":"の後の文字列を削除するには、 "cut"を使用します。その後、ファイルパーツを取得します(ファイルパスにコロンまたは改行文字が含まれず、2番目のパターン自体と一致しないと仮定します)。

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results" | cut -d ":" -f 1

重複する場合は「uniq」を使用します

grep -ir "string1" . | grep "string2" | cut -d: -f1 | uniq
5
Brian SP2

grep-lオプションを指定すると、指定したパターンを含むファイルの名前のみが印刷されます。私のシステムのマニュアルでは、このオプションについて次のように述べています。

選択された行を含むファイルの名前のみが標準出力に書き込まれます。 grepは、一致が見つかるまでファイルを検索するだけなので、検索のコストが低くなる可能性があります。パス名は、検索されるファイルごとに1回リストされます。標準入力を検索すると、「(標準入力)」という文字列が書き込まれます。

パイプラインの2番目のgrepは、ファイルからではなく標準入力から読み取っているので、標準入力ストリームに到着している以外のデータがどこから来ているのかを認識していません。これが、テキスト文字列(standard input)を返す理由です(質問に記載されている(standard output)ではありません)。これは、試合が行われた場所に到達できる限り近くなります。

最初のgrepで2つのパターンを組み合わせるには(これはを実行しますはどのファイルを探しているかについての知識があります)、「 複数のANDパターンを使用してgrepを実行する方法 "

15
Kusalananda

(私はあなたがあなたのアプローチが行っていたように、2番目のgrepがファイルの名前ではなく行の内容で一致することを意図していたと仮定しています)

POSIXly:

find . -type f -exec awk '
  FNR == 1 {found = 0}
  !found && tolower($0) ~ /some string/ && /other string/ {
    print FILENAME
    found = 1
    nextfile
  }' {} +

foundに関するビジネスは、nextfileをまだサポートしていないawk実装用です(nextfileは何もしない場合)。 awk実装がnextfileをサポートしていることがわかっている場合は、次のように簡略化できます。

 find . -type f -exec awk 'tolower($0) ~ /some string/ && /other string/ {
    print FILENAME; nextfile}' {} +

GNU grepはPCREサポートを使用して構築されています。一方の一致では大文字と小文字を区別せず、もう一方の一致ではないためです。

grep -rlP '^(?=.*(?i:some string))(?=.*other string)' .

(?=...)はPerl look-ahead演算子です。 (?i:pattern)patternに対してのみ大文字と小文字を区別しない一致をオンにします。したがって、ここでは、行の先頭(^)に一致しています。ただし、その後に任意の数の文字(.*)が続き、その後にsome string(大文字と小文字は区別されません)が続きます。行の先頭)の後には、任意の数の文字とother string(大文字と小文字を区別)が続きます。

grep-Pをサポートしていない場合は、代わりにpcregrepコマンドを使用するか(grep -rlPpcregrep -rlに置き換えてください)、またはパターンが重複しないでください:

grep -rl -e '[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG].*other string' \
         -e 'other string.*[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG]' .

または、両方の一致で大文字と小文字が区別されなくてもかまいません。

grep -ril -e 'some string.*other string' \
          -e 'other string.*some string' .
5

次のパターンを使用して、両方のパターンを1つのクエリに配置できます(これに基づいて answer )。

grep -P '^(?=.*pattern1)(?=.*pattern2)'

あなたの場合、次の形式で-ir -lパラメータを追加できます。

grep -irlP '^(?=.*pattern1)(?=.*pattern2)' .
2
Yaron

これは、提供されるすべての中で最も短いソリューションです。

find . -type f -exec Perl -lne '
   /Some string/i and /other string/ and print($ARGV),close(*ARGV);
' {} +

grep -irZ "Some string" . |
Perl -lsF'/\n/' -0ne '
   s/^/\n/ if $. == 1; s/$/\n/ if eof;

   $. == 1 and $prev = $F[1],next;
   Push @{$h{$prev}}, $F[0];
   $prev = $F[1];

   END {
      grep($_ =~ /\Q${str2}/, @{$h{$_}}) and print for keys %h;
   }
' -- -str2="Another string"

動作原理:ここで、最初のgreprecursivecase-insensitiveを実行して、現在のディレクトリおよびそれ以降の「文字列」を検索し、null区切り(\0)を生成します。 grepに指定された-Zオプションによるレコード。

これらの各レコードには、ファイル名と一致した行が含まれています。唯一の問題は、一致する行の後に\0を付けないというgrepの動作のために、順序が合っていないことです。この制限を回避するために、ヌル区切りレコードを読み取るPerlを利用し、これらのレコードを\nで分割して、ファイル名から行を分離します。

したがって、禁止されている\0を除いて、ファイル名の種類に制限はありません。

1
user218374