1行の2つの文字列をgrepで照合する


218

grep2つの異なる文字列を含む行を一致させるために使用しようとしています。私は次のことを試しましたが、これはstring1 または string2のいずれかを含む行と一致しますが、これは必要ではありません。

grep 'string1\|string2' filename

それでgrep両方の文字列を含む行だけとどのように一致しますか?


回答:


189

使用できます grep 'string1' filename | grep 'string2'

または、 grep 'string1.*string2\|string2.*string1' filename


5
@AlexanderN確かに、マルチラインで動作させることはできません。これは奇妙なことに受け入れられました。–
Aquarius Power

1
複数行の質問ではありませんでした。複数行の場合、grep -PはPerlスタイルの正規表現をサポートします...
Scott Prive 2014年

20
「string1」と「string2」の両方が同じ行にある場合にのみ機能します。「string1」または「string2」のいずれかの行を検索する場合は、user45949の回答を参照してください。
lifeson106

10
最初のオプション:1つのgrepを別のgrepにパイプすると、OR結果が生成されず、AND結果が生成されます。
masukomi

1
私が使用しましたgrep -e "string1" -e "string2"
Ravi Dhoriyaツ

198

これはあなたが探していたものだと思います:

grep -E "string1|string2" filename

私はそれがこのように答えると思います:

grep 'string1.*string2\|string2.*string1' filename

両方が存在する場合にのみ一致し、どちらか一方または両方は一致しません。


14
grep -e "string1" -e "string2" filename同じことをしませんか?
janosdivenyi 2015

25
これはstring1 OR string2をgrepする方法です。質問は明らかに、string1とstring2を探していることを示しています。
orion elenzil 2015年

9
質問がかなり正確であることを確認してください:How do I match lines that contains *both* strings?
r0estir0bbe

同じ行で印刷できますか?
吴毅凡

1
なぜこの答えがまだここにあるのですか?質問に対する答えではありません。
Prometheus

26

すべての単語を含むファイルを任意の順序で検索するには、次の手順を実行します。

grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'

最初のgrepは、再帰検索(r)を開始します。大文字と小文字を区別せず(i)、ファイル内の任意の場所にあるl1つの用語('action'一重引用符で)に一致する()ファイルの名前をリスト(出力)します。

後続のgrepsは他の用語を検索し、大文字と小文字を区別せずに一致するファイルをリストします。

取得されるファイルの最終的なリストは、これらの用語を含むファイルで、ファイル内の任意の場所に任意の順序で表示されます。


2
同意しました!スペースを含むファイル名を処理するには、xargsに "-d '\ n'"を指定する必要があったことに注意してください。これはLinuxで私にとってうまくgrep -ril 'foo' | xargs -d '\n' grep -il 'bar'
Tommy Harris

16

あなたが持っている場合grep-P限られたためのオプションperlの正規表現は、使用することができます

grep -P '(?=.*string1)(?=.*string2)'

これには、重複する文字列を処理するという利点があります。andロジックをより直接指定できるため、perlas を使用する方が多少簡単grepです。

perl -ne 'print if /string1/ && /string2/'

1
ベストアンサー。シェルは非常に簡単で迅速ですが、パターンが複雑になったら、PythonまたはPerl(またはAwk)を使用する必要があります。それが純粋なシェルでできることを証明しようとして壁に頭をぶつけないでください(それが最近何を意味するにせよ)。これらのツールは、既存のシェルスクリプトにディブルを埋め込む「ワンライナー」構文で使用できます。
Scott Prive 2014年

12

あなたの方法はほぼ良好で、-wが欠けているだけです

grep -w 'string1\|string2' filename

1
少なくともOS-XとFreeBSDでは動作します!私の推測では、あなたは何か他のものにいると思います(OPは定義していません-あなた以外の多くのユーザーに正しい答えを投票しないことを願っています)。
Leo

OS-Xを使用しています。おそらく私はこれを正しく行っていませんか?私がやったことを見てください:i.imgur.com/PFVlVAG.png
Ariel

1
奇数。違いはファイルにgreppingしないことであると予想しましたが、自分のメソッドをlsでパイプ処理すると、次の結果が得られません:imgur.com/8eTt3Ak.png-両方のOS-X 10.9.5( 「grep(BSD grep)2.5.1-FreeBSD」)およびFreeBSD 10(「grep(GNU grep)2.5.1-FreeBSD」)。私はあなたgrep -Vが何であるか知りたいです。
レオ

1
あなたの例は私のために働いています:i.imgur.com/K8LM69O.pngしたがって、違いは、このメソッドは部分文字列を取得しないということです。それ自体は完全な文字列でなければなりません。部分文字列を検索するには、grep内で正規表現を作成する必要があると思います。次のようなもの:grep -w 'regexp1\|regexp2' filename
アリエル、

2
OPは、string1 または string2を照合する例を示し、両方の文字列を含む行を照合する方法を尋ねます。この例でもORが生成されます。
gustafbstrom 2016年

7

|正規表現の演算子は、orを意味します。つまり、string1またはstring2のいずれかが一致します。あなたがすることができます:

grep 'string1' filename | grep 'string2'

最初のコマンドの結果を2番目のgrepにパイプします。両方に一致する行のみが表示されます。


1
あなたの発言は真実ですが、OPの質問には答えないでください
ベンウィーラー

これは質問に答えるものであり、これは確かにほとんどの人がそれをどのように書くかです。
Peter K

7

あなたはこのようなことを試すことができます:

(pattern1.*pattern2|pattern2.*pattern1)

4

そして人々がperlとpython、そして複雑なシェルスクリプトを提案したように、ここで単純なawkアプローチ:

awk '/string1/ && /string2/' filename

受け入れられた回答へのコメントを見て、いいえ、これは複数行を実行しません。しかし、それはまた、質問の作者が求めたことではありません。


3

これにはgrepを使用しないでください。代わりにawkを使用してください。grepで2つの正規表現R1とR2を一致させるには、次のようになります。

grep 'R1.*R2|R2.*R1'

awk中は次のようになります。

awk '/R1/ && /R2/'

しかし、R2と重複する場合、またはサブセットである場合はR1どうなりますか?そのgrepコマンドは単にawkコマンドが機能する一方で機能しません。theand を含む行を検索するとしますheat

$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre

そのためには、2つのgrepsと1つのパイプを使用する必要があります。

$ echo 'theatre' | grep 'the' | grep 'heat'
theatre

もちろん、実際にそれらを分離する必要があった場合は、常にgrepで使用したものと同じ正規表現をawkに書き込むことができます。可能なすべてのシーケンスで正規表現を繰り返すことを含まない代替のawkソリューションがあります。

それはさておき、3つの正規表現R1、R2、およびR3に一致するようにソリューションを拡張したい場合はどうでしょうか。grepでは、これはこれらの悪い選択の1つになります。

grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3

awkでは、簡潔で、明白で、単純で、効率的です。

awk '/R1/ && /R2/ && /R3/'

では、正規表現R1とR2の代わりにリテラル文字列S1とS2を実際に一致させたい場合はどうでしょうか。grepを1回呼び出すだけではこれを実行できません。grepを呼び出す前に、すべてのREメタ文字をエスケープするコードを記述する必要があります。

S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'

または再度2つのgrepsとパイプを使用します。

grep -F 'S1' file | grep -F 'S2'

これも選択肢としては不十分ですが、awkでは正規表現演算子の代わりに文字列演算子を使用するだけです。

awk 'index($0,S1) && index($0.S2)'

では、段落ではなく行ではなく2つの正規表現を一致させたい場合はどうでしょうか。grepではできません。awkでは簡単です。

awk -v RS='' '/R1/ && /R2/'

ファイル全体でどうですか?繰り返しになりますが、grepおよびawkのささいなことでは実行できません(今回は、簡潔にするために複数文字のRSにGNU awkを使用していますが、awkのコードはそれほど多くありません。 RSが同じことをするための入力に含まれる):

awk -v RS='^$' '/R1/ && /R2/'

したがって、行、段落、またはファイルで複数の正規表現または文字列を検索する場合は、grepを使用せずに、awkを使用してください。


あるawk '/R1/ && /R2/'大文字と小文字を区別しませんか?
プロメテウス

@ハシム-いいえ。GNU awk awk -v IGNORECASE=1 '/R1/ && /R2/'と他のawk で大文字と小文字を区別しないようにするにはawk '{x=toupper($0)} x~/R1/ && x~/R2/'
Ed Morton


2

スペースが6つだけで始まり、次で終わる行が見つかりました:

 cat my_file.txt | grep
 -e '^      .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
 -e '^      .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
 > nolog.txt

2

ファイルtestfile内の複数の単語の数を見つける必要があるとしましょう。それを行うには2つの方法があります

1)正規表現一致パターンでgrepコマンドを使用します

grep -c '\<\(DOG\|CAT\)\>' testfile

2)egrepコマンドを使用する

egrep -c 'DOG|CAT' testfile 

egrepを使用すると、式を気にする必要がなく、単語をパイプ区切り文字で区切るだけで済みます。


2

git grep

git grep複数のパターンで使用する構文は次のとおりです。

git grep --all-match --no-index -l -e string1 -e string2 -e string3 file

パターンをや、などのブール式と組み合わせることもできます。--and--or--not

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


--all-match複数のパターン式を指定する場合、このフラグを指定して、すべてに一致する行があるファイルへの一致を制限します

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

-l/ --files-with-matches/ --name-onlyファイルの名前のみを表示します。

-e次のパラメーターはパターンです。デフォルトでは、基本的な正規表現を使用します。

考慮すべきその他のパラメータ:

--threads 使用するgrepワーカースレッドの数。

-q/ --quiet/ --silent一致した行を出力しません。一致する場合、ステータス0で終了します。

パターンの種類を変更するには、あなたも使用することができます-G/ --basic-regexp(デフォルト)、-F/ --fixed-strings-E/ --extended-regexp-P/ --perl-regexp-f file、およびその他。

関連:

OR演算については、以下を参照してください。


2
「git grep」はgitリポジトリ内でのみ実行できると常に考えていました。--no-indexオプションを認識していませんでした。指摘してくれてありがとう!
カマラジュクスマンチ

1

grepしたい文字列をファイルに配置します

echo who    > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt

次に、-fを使用して検索します

grep -f find.txt BIG_FILE_TO_SEARCH.txt 

1
grep '(string1.*string2 | string2.*string1)' filename

string1とstring2の行を任意の順序で取得します


少なくとも上位2つの回答とはどのように異なりますか?
luk2302

1
grep -i -w 'string1\|string2' filename

これは、完全な単語の一致と大文字と小文字を区別しない単語の一致で機能し、そのため-iが使用されます


0

複数行一致の場合:

echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"

または

echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"

改行文字を削除するだけで機能します。


0

あなたはgrepこのようにする必要があります:

$ grep 'string1' file | grep 'string2'

1
これは論理ANDを実行します。OPは論理ORを必要としています。
ベンウィーラー

1
@BenWheeler:質問から:「両方の文字列を含む行だけをgrepと一致させるにはどうすればよいですか?」
エリックI

0

私はよくあなたと同じ問題に出くわします、そして私はスクリプトの一部を書いただけです:

function m() { # m means 'multi pattern grep'

    function _usage() {
    echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
    echo "-i : ignore case"
    echo "-n : show line number"
    echo "-H : show filename"
    echo "-h : show header"
    echo "-p : specify pattern"
    }

    declare -a patterns
    # it is important to declare OPTIND as local
    local ignorecase_flag  filename linum header_flag colon result OPTIND

    while getopts "iHhnp:" opt; do
    case $opt in
        i)
        ignorecase_flag=true ;;
        H)
        filename="FILENAME," ;;
        n)
        linum="NR," ;;
        p)
        patterns+=( "$OPTARG" ) ;;
        h)
        header_flag=true ;;
        \?)
        _usage
        return ;;
    esac
    done

    if [[ -n $filename || -n $linum ]]; then
    colon="\":\","
    fi

    shift $(( $OPTIND - 1 ))

    if [[ $ignorecase_flag == true ]]; then
    for s in "${patterns[@]}"; do
            result+=" && s~/${s,,}/"
    done
    result=${result# && }
    result="{s=tolower(\$0)} $result"
    else
    for s in "${patterns[@]}"; do
            result="$result && /$s/"
    done
    result=${result# && }
    fi

    result+=" { print "$filename$linum$colon"\$0 }"

    if [[ ! -t 0 ]]; then       # pipe case
    cat - | awk "${result}"
    else
    for f in "$@"; do
        [[ $header_flag == true ]] && echo "########## $f ##########"
        awk "${result}" $f
    done
    fi
}

使用法:

echo "a b c" | m -p A 
echo "a b c" | m -i -p A # a b c

必要に応じて、.bashrcに配置できます。


0

両方の文字列が連続している場合は、grepコマンドの間にパターンを配置します。

$ grep -E "string1(?.*)string2" file

次の行がというファイルに含まれている場合の例Dockerfile

FROM python:3.8 as build-python
FROM python:3.8-slim

文字列を含む行を取得するには、次のFROM pythonas build-python、次に使用します。

$ grep -E "FROM python:(?.*) as build-python" Dockerfile

次に、出力には両方の文字列を含む行のみが表示されます

FROM python:3.8 as build-python

-2

ripgrep

以下は使用例rgです。

rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt

Rustの正規表現エンジンの上に構築されているため、最速のgreppingツールの1つです。有限オートマトン、SIMD、および積極的なリテラル最適化を使用して検索を非常に高速にするです。

特に、大きなデータを扱う場合に使用してください。

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


1
この答えは正しくありません。名前付きのキャプチャグループは不要であり、string2がの前に表示される場合は、これを処理しませんstring1。この問題の最も簡単な解決策はrg string1 file.txt | rg string2です。
BurntSushi5
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.