web-dev-qa-db-ja.com

複数のANDパターンでgrepを実行するにはどうすればよいですか?

パターン間で暗黙的な[〜#〜] and [〜#〜]を使用してマルチパターンマッチを取得します。つまり、シーケンスで複数のgrepsを実行するのと同じです。

grep pattern1 | grep pattern2 | ...

それをどのように変換するのですか?

grep pattern1 & pattern2 & pattern3

動的に引数を作成するため、単一のgrepを使用したいので、すべてが1つの文字列に収まる必要があります。フィルターの使用はシステム機能であり、grepではないため、引数ではありません


この質問を以下と混同しないでください。

grep "pattern1\|pattern2\|..."

これは[〜#〜] or [〜#〜]マルチパターンマッチです。

91
greenoldman

agrepは次の構文で実行できます。

agrep 'pattern1;pattern2'

GNU grepを使用すると、PCREサポートを使用してビルドすると、次のことができます。

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

ast grep の場合:

grep -X '.*pattern1.*&.*pattern2.*'

.*<x>&<y>として追加すると、<x><y>の両方に一致する文字列に一致しますexactlya&bは、abの両方を同時にbeできる文字列がないため、決して一致しません時間)。

パターンが重ならない場合は、次のことも可能です。

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

すでに述べたように、最も移植性のある方法はおそらくawkを使用することです。

awk '/pattern1/ && /pattern2/'

sedの場合:

sed -e '/pattern1/!d' -e '/pattern2/!d'

これらはすべて異なる正規表現構文を持つことに注意してください。

85

あなたはgrepバージョンを指定しませんでした、これは重要です。一部の正規表現エンジンでは、「&」を使用してANDでグループ化された複数の一致が許可されますが、これは非標準で移植性のない機能です。しかし、少なくともGNU grepはこれをサポートしていません。

OTOHでは、grepをsed、awk、Perlなどに簡単に置き換えることができます(重量の増加順にリストされています)。 awkでは、コマンドは次のようになります。

 awk '/ regexp1/&&/regexp2/&&/regexp3/{print; } '

そしてそれは簡単な方法でコマンドラインで指定するように構築することができます。

19
Netch

これはあまり良い解決策ではありませんが、ややクールな「トリック」を示しています

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont
8
olejorgenb

patternsに1行に1つのパターンが含まれている場合、次のようなことができます:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

または、これは正規表現ではなく部分文字列に一致します。

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

patternsが空の場合に入力行がない代わりにすべて印刷するには、NR==FNRFILENAME==ARGV[1]に、またはgawkARGIND==1に置き換えます。 。

これらの関数は、引数として指定された各文字列を部分文字列として含むSTDINの行を出力します。 gaはgrep allを表し、gaiは大文字と小文字を区別しません。

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }
7
nisetama

git grep

git grep を使用した構文は次のとおりですBoolean式を使用して複数のパターンを組み合わせます。

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

上記のコマンドは、すべてのパターンに一致する行を一度に印刷します。

--no-index Gitによって管理されていない現在のディレクトリ内のファイルを検索します。

man git-grepを確認してください。

以下も参照してください。

[〜#〜]または[〜#〜]演算については、以下を参照してください。

4
kenorb

これが私の見解であり、これは複数行の単語に対して機能します。

使用する find . -type fの後に
-exec grep -q 'first_Word' {} \;
と最後のキーワード
-exec grep -l 'nth_Word' {} \;

-q静か/静か
-l一致するファイルを表示

次の例では、「rabbit」と「hole」という単語を含むファイル名のリストを返します。
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

2
StackRover

ripgrep

rg を使用した例を次に示します。

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

Rustの正規表現エンジン の上に構築されているため、これは最も高速なgreppingツールの1つです。

GH-875 の関連する機能リクエストも参照してください。

1
kenorb

このシェルコマンドが役立つことがあります。

eval "</dev/stdin $(printf "|grep '%s'" pattern1 pattern2)" FILE

ただし、すべてのパターンがファイルに格納されている方が簡単なので、次のエイリアスを定義できます。

alias grep-all="cat $(xargs printf '| grep "%s"' < patterns.txt)"

そしてそれを次のように使用します:

cat text.txt | grep-all

もちろん、必要な構文に応じてエイリアスを変更することができるため、次のエイリアスを使用します。

alias grep-all="</dev/stdin $(xargs printf '|grep "%s"' < patterns.txt)"

1つのコマンドだけを使用できます。

grep-all text.txt

その他のアイデアについては、次も確認してください: ファイルのすべてのパターンを一度に一致させます

ファイルごとのAND演算については、 ファイルに複数の文字列または正規表現がすべて存在するかどうかを確認してください

1
kenorb