パイプ文字を持つパターンで複数のパターンをgrepするにはどうすればよいですか?


624

2つのパターンのいずれかに一致する複数のファイルですべての行を検索したい。入力して探しているパターンを見つけようとしました

grep (foo|bar) *.txt

しかし、シェルは|をパイプとして解釈しbar、実行可能ファイルではない場合に文句を言います。

同じファイルセットで複数のパターンをgrepするにはどうすればよいですか?



grep 'word1 \ | word2 \ | word3' / path / to / file
lambodar

回答:


861

最初に、シェルによる拡張からパターンを保護する必要があります。最も簡単な方法は、単一引用符で囲むことです。単一引用符は、それらの間の何か(バックスラッシュを含む)の展開を防ぎます。できないのは、パターン内に単一引用符を付けることだけです。

grep 'foo*' *.txt

単一引用符が必要な場合は、'\''(終了文字列リテラル、リテラル引用、オープン文字列リテラル)と書くことができます。

grep 'foo*'\''bar' *.txt

次に、grepはパターンの2つの構文をサポートします。古いデフォルトの構文(基本的な正規表現)は代替(|)演算子をサポートしていませんが、一部のバージョンでは拡張機能として使用されていますが、バックスラッシュで記述されています。

grep 'foo\|bar' *.txt

移植可能な方法は、新しい構文である拡張正規表現を使用することです。-Eオプションを渡して選択する必要がありますgrep。Linuxでは、egrep代わりに入力することもできますgrep -E(他の大学では、エイリアスにすることができます)。

grep -E 'foo|bar' *.txt

(選言を使用して複雑なパターンを構築するのではなく)いくつかのパターンのいずれかを探している場合の別の可能性は、複数のパターンをに渡すことgrepです。これを行うには、各パターンの前に-eオプションを付けます。

grep -e foo -e bar *.txt

18
追記として-パターンが固定されているとき、あなたは本当にの習慣を身に取得する必要fgrepgrep -F、小さなパターンのための違いは無視できる程度になりますが、彼らは長くなるとして、メリットは...表示するために開始
TC1

7
@ TC1のfgrepははmanページに応じて推奨されていません
RAMN

18
@ TC1 grep -F実際のパフォーマンス上の利点があるかどうかは、grepの実装に依存します。それらのいくつかは、とにかく同じアルゴリズムを適用します-F-Fたとえば、GNU grepはで高速ではありません(grep -Fマルチバイトロケールで低速になるバグもあります。同じ定数パターンgrepは実際にはかなり高速です!)。一方、BusyBoxのgrepは-F、大きなファイルから多くのメリットを得ることができます。
ジル14

4
おそらく、代替が正規表現の一部のみであるより複雑なパターンの場合、「\(」および「\)」でグループ化できることに注意する必要があります(エスケープはデフォルトの「基本正規表現」 )(?)
ピーターモーテンセン

4
egreppredatesに注意してくださいgrep -E。GNU固有ではありません(Linuxとはまったく関係ありません)。実際には、デフォルトがgrepまだサポートしていないSolarisのようなシステムがまだあります-E
ステファンシャゼラス

90
egrep "foo|bar" *.txt

または

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

gnu-grepのmanページを選択的に引用:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

最初はこれ以上読みませんでしたので、微妙な違いを認識しませんでした。

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

例から学んだので、私は常にegrepと不必要に括弧を使用しました。今、私は何か新しいことを学びました。:)


22

TC1が言った-Fように、使用可能なオプションのようです:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige $ 'foo \ nbar'オプションについては知りませんでした。ここで展開がどのように機能するのかわかりません。検索する必要がありますが、本当に便利です。
haridsv

いいね!また、このオプションを使用すると、正規表現が無効になるため、実行速度が大幅に向上するようです。
qwertzguy

15

まず、特殊文字には引用符を使用する必要があります。第二に、たとえそうであっても、grep交代を直接理解することはありません。egrepまたは(GNU grepのみ)を使用する必要がありますgrep -E

egrep 'foo|bar' *.txt

(代替がより大きな正規表現の一部でない限り、括弧は不要です。)


4
実際、grep -Eはより標準的ですegrep
jw013

8

正規表現が必要ない場合は、次のように、fgrepまたはgrep -F複数の-eパラメーターを使用する方がはるかに高速です。

fgrep -efoo -ebar *.txt

fgrep(代替grep -F)は、正規表現ではなく固定文字列を検索するため、通常のgrepよりもはるかに高速です。


4
このページに記載fgrepされている非推奨のコメントも参照してください。
phk 16


3

複数のパターンをgrepする安価で陽気な方法:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

説明から恩恵を受けることができます。
ピーターモーテンセン

2
説明は、grepの-fオプションが複数のパターンを持つファイルを取るということです。一時ファイル(後で削除するのを忘れることがあります)を作成する代わりに、シェルのプロセス置換を使用するだけです:grep -f <(echo foo; echo bar) *.txt
Jakob

3

パイプ(|)は特殊なシェル文字であるため、エスケープ(\|)するか、マニュアルに従って引用符で囲む必要があります(man bash)。

引用は、シェルに対して特定の文字または単語の特別な意味を削除するために使用されます。特殊文字の特別な処理を無効にし、予約語がそのように認識されるのを防ぎ、パラメータの拡張を防ぐために使用できます。

文字を二重引用符で囲むと、引用符内のすべての文字のリテラル値保持されます

引用符で囲まれていないバックスラッシュ(\)はエスケープ文字です。

参照:Bashでエスケープする必要がある文字はどれですか?

以下にいくつかの例を示します(まだ言及されていないツールを使用):

  • を使用してripgrep

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • を使用してgit grep

    • git grep --no-index -e foo --or -e bar

      注:--and、などのブール式もサポート--orしてい--notます。

行ごとのAND演算については、複数のANDパターンでgrepを実行する方法をご覧ください

ファイルごとのAND演算については、次を参照してください。ファイルに存在する複数の文字列または正規表現をすべて確認する方法


3

日付が愚かにフォーマットされたアクセスログがありました:[30 / Jun / 2013:08:00:45 +0200]

しかし、次のように表示する必要がありました:30 / Jun / 2013 08:00:45

問題は、grepステートメントで「OR」を使用すると、2つの別々の行で2つの一致式を受け取っていたことです。

解決策は次のとおりです。

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR:複数のパターンのいずれかに一致した後、さらに処理を行う場合は、次のように囲みます \(pattern1\|pattern2\)

例:「date」という名前を含む変数がStringまたはintとして定義されているすべての場所を検索したい。(例: "int cronDate ="または "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

を使用grep -Eすると、括弧やパイプをエスケープする必要がありません。つまり、grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

これは私のために働く

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

これを行うには複数の方法があります。

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

3番目と4番目のオプションは、ファイルでのみgrepを実行.txtし、名前にディレクトリが含まれないようにします。
したがって、ユースケースに従って、上記のオプションのいずれかを使用できます。
ありがとう!!


0

@geekosaurの答えに追加するには、タブとスペースも含む複数のパターンがある場合は、次のコマンドを使用します

grep -E "foo[[:blank:]]|bar[[:blank:]]"

where [[:blank:]]は、スペースまたはタブ文字を表すRE文字クラスです

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