web-dev-qa-db-ja.com

n桁のグループ(nを超えない)をgrepする方法は?

私はLinuxを学んでいますが、自分で解決できないように思える課題があります。ここにあります:

grepは、1行に4つの数字を含むが4つ以下のファイルから1行をgrepします。

私はこれにどのようにアプローチするかわかりません。特定の数値を検索することはできますが、文字列でその数値を検索することはできません。

33
Buddha

この質問を解釈するには2つの方法があります。両方のケースに対処します。行を表示したい場合があります。

  1. それ自体が長い数字のシーケンスの一部ではない4桁のシーケンスを含むor
  2. これには4桁のシーケンスが含まれていますが、数字のシーケンスは含まれていません(個別にではありません)。

たとえば、(1)は1234a56789を表示しますが、(2)は表示しません。


それ自体が長い数字のシーケンスの一部ではない4桁のシーケンスを含むすべての行を表示する場合、1つの方法は次のとおりです。

grep -P '(?<!\d)\d{4}(?!\d)' file

これは Perl正規表現 を使用します。これはUbuntuの grepGNU grep )が-Pを介してサポートします。 12345のようなテキストには一致しません。また、その一部である12342345にも一致しません。 ただし、12341234a56789と一致します。

Perlの正規表現では:

  • \dは任意の数字を意味します([0-9]または[[:digit:]]と言うのは簡単な方法です)。
  • x{4}x 4回と一致します。 ({}構文はPerl正規表現に固有のものではありません。grep -Eを介した拡張正規表現にもあります。)\d{4}\d\d\d\dと同じです。
  • (?<!\d)は、幅がゼロの負の後ろ読みアサーションです。 「\dが前にない限り」という意味です。
  • (?!\d)は、幅がゼロの負の先読みアサーションです。 「その後に\dが続かない限り」という意味です。

(?<!\d)および(?!\d)は、4桁のシーケンス外のテキストと一致しません。代わりに、より長い一連の数字の一部である場合、(一緒に使用されると)4桁の数字が一致するのを防ぎます。

右端または左端の4桁のサブシーケンスがまだ一致するため、後読みのみまたは先読みのみを使用するだけでは不十分です。

look-behindおよびlook-ahead assertions を使用する利点の1つは、パターンが周囲のテキストではなく、4桁のシーケンスのみと一致することです。これは、色の強調表示(--colorオプションを使用)を使用する場合に役立ちます。

ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4

デフォルト Ubuntuでは、各ユーザーは alias grep='grep --color=auto'ファイル~.bashrcを持っています。したがって、grepで始まる単純なコマンドを実行すると(これは aliases が展開されるとき)、 標準出力a terminal であるときに、自動的に色が強調表示されます(これは --color=auto がチェックするものです)。一致は通常、赤の影で強調表示されます( vermilion に近い)が、斜体の太字で示しています。 スクリーンショットはこちら
Screenshot showing that grep command, with 12345abc789d0123e4 as output, with the 0123 highlighted in red.

また、-oを使用して、行全体ではなく、一致するテキストのみをgrepに出力させることもできます。

ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123

別の方法、Without Look-BehindおよびLook-Aheadアサーション

ただし、次の場合:

  1. grep-Pをサポートしていないシステム、またはPerl正規表現を使用したくないシステムで実行されるコマンドが必要ですand
  2. 特に4桁の数字と一致する必要はありません。通常、一致する行を表示するだけの場合はそうです。and
  3. 少しエレガントではないソリューションで大丈夫です

...次に、代わりに 拡張正規表現 を使用してこれを実現できます。

grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file

これは、それらを囲む4桁の数字と数字以外の文字(または行の先頭または末尾)に一致します。具体的には:

  • [0-9]は任意の数字(Perl正規表現の[[:digit:]]\dなど)と一致し、{4}は「4回」を意味します。したがって、[0-9]{4}は4桁のシーケンスに一致します。
  • [^0-9]は、0から9の範囲にない文字に一致します。 [^[:digit:]](またはPerl正規表現では\D)と同等です。
  • ^は、[]括弧内に表示されない場合、行の先頭に一致します。同様に、$は行末に一致します。
  • |orを意味し、括弧はグループ化用です(代数のように)。したがって、(^|[^0-9])は行の先頭または数字以外の文字に一致し、($|[^0-9])は行の末尾または数字以外の文字に一致します。

したがって、一致は、同時に4桁のシーケンス([0-9]{4})を含む行でのみ発生します。

  • 行の先頭または非数字((^|[^0-9]))の前にand
  • 行末または非数字(($|[^0-9]))が続きます。

一方、4桁のシーケンスを含むが、4桁を超えるanyシーケンスを含まないすべての行を表示する場合(他のシーケンスのみとは別の行でも) 4桁)、概念的には、1つのパターンには一致するが別のパターンには一致しない行を見つけることが目標です。

したがって、1つのパターンでそれを行う方法を知っていたとしても、2つのパターンに対して別々に matt's 2番目の提案、grepingのようなものを使用することをお勧めします。

Perlの正規表現の高度な機能は、それを行う際に大きなメリットを享受しないため、使用しない方がよい場合があります。しかし、上記のスタイルに合わせて、 mattの解決策 の短縮形を以下に示します。\dの代わりに[0-9](および中括弧)を使用します。

grep -P '\d{4}' file | grep -Pv '\d{5}'

[0-9]を使用しているため、 matt's way はより移植性が高く、grepがPerlの正規表現をサポートしていないシステムで動作します。 [0-9]の代わりに[[:digit:]](または\d)を使用し、{}を引き続き使用すると、mattの方法の移植性がもう少し簡潔になります。

grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'

別の方法、単一パターン

本当にgrepコマンドを好む場合

  1. 単一の正規表現を使用(上記のように pipe で区切られた2つのgrepsではありません)
  2. 4桁のシーケンスを少なくとも1つ含む行を表示するには、
  3. ただし、5桁以上のシーケンスはありません。
  4. そして、数字だけでなく、行全体を一致させることを気にしません(おそらくこれを気にしません)

...次に使用できます:

grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file

-xフラグを使用すると、grepは、行全体が一致する行のみを表示します(行含むではなく)。

\d\Dの簡潔さがこの場合の明確さを大幅に向上させると思うので、Perl正規表現を使用しました。ただし、grep-Pをサポートしないシステムに移植可能なものが必要な場合は、それらを[0-9]および[^0-9](または[[:digit:]]および[^[:digit]])に置き換えることができます。

grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file

これらの正規表現の機能は次のとおりです。

  • 中央では、\d{4}または[0-9]{4}は4桁の1つのシーケンスに一致します。これらは複数ある場合もありますが、少なくとも1つ必要です。

  • 左側の(\d{0,4}\D)*または([0-9]{0,4}[^0-9])*は、4桁以下で、その後に非数字が続く0個以上の(*)インスタンスに一致します。ゼロ桁(つまり、何もない)は、「4桁以下」の可能性の1つです。これは(a)空の文字列または(b)任意の文字列endingに一致し、4桁以上のシーケンスを含まない桁。

    中央の\d{4}(または[0-9]{4})のすぐ左のテキストは空であるか、数字以外で終わる必要があるため、中央の\d{4}がそのすぐ左に別の(5番目の)数字がある4桁と一致しないようにします。

  • 右側の(\D\d{0,4})*または([^0-9][0-9]{0,4})*は、ゼロ以上の(*)インスタンスに一致し、その後に4桁以下の数字が続きます(以前と同様に、4、3、2、1、またはまったくない場合もあります)。これは、(a)空の文字列または(b)任意の文字列beginningに一致し、4桁以上のシーケンスを含まない桁。

    中央の\d{4}(または[0-9]{4})のすぐ右にあるテキストは空にするか、数字以外で始まる必要があるため、中央の\d{4}が、そのすぐ右側に別の(5番目の)数字がある4桁と一致しないようにします。

これにより、4桁のシーケンスがどこかに存在し、5桁以上のシーケンスがどこにも存在しないことが保証されます。

この方法で行うことは悪くも間違っていません。しかし、おそらくこの代替案を検討する最も重要な理由は、上記および matt's answer で提案されているように、代わりにgrep -P '\d{4}' file | grep -Pv '\d{5}'(または同様の)を使用する利点を明確にすることです。

そのようにして、あなたの目標は、あるものを含むが別のものを含まない行を選択することは明らかです。さらに、構文はより単純です(したがって、多くの読者/保守者がより早く理解できるかもしれません)。

49
Eliah Kagan

これにより、4つの数字が連続して表示されますが、それ以上は表示されません

grep '[0-9][0-9][0-9][0-9][^0-9]' file

^は意味しないことに注意してください

これには問題がありますが、修正方法がわかりません...番号が行末である場合は表示されません。

ただし、このいバージョンはその場合に機能します

grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]
8
matt

grepがPerlの正規表現(-P)をサポートしていない場合、次のシェルコマンドを使用します。

grep -w "$(printf '[0-9]%.0s' {1..4})" file

ここで、printf '[0-9]%.0s' {1..4}は4回[0-9]を生成します。この方法は、長い数字があり、パターンを繰り返したくない場合に便利です(4を自分の数字の数に置き換えるだけです)。

-wを使用すると、単語全体が検索されます。ただし、1234aなどの英数字文字列に関心がある場合は、パターンの最後に[^0-9]を追加します。

grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file

$()の使用は、基本的に コマンド置換 です。これを確認してください postprintfがパターンを繰り返す方法を確認してください。

0
kenorb

システム内のファイルの実際のファイル名を置き換えることにより、以下のコマンドを試すことができます。grepコマンドのその他の使用法については、 このチュートリアル も確認できます。

grep -E '(^ | [^ 0-9])[0-9] {4}($ | [^ 0-9])'ファイル

0
Mike Tyson