私は Linux を学習中ですが、自力では解決できない課題があります。その課題は次のとおりです。
ファイルから、連続する 4 つの数字 (ただし 4 つ以下) を含む行を grep します。
これにどうアプローチしたらよいかわかりません。特定の数字は検索できますが、文字列内の数字の量は検索できません。
答え1
この質問には 2 つの解釈方法があります。両方のケースについて説明します。線を表示したい場合があります。
- それ自体はそれ以上の数字列の一部ではない4桁の数字列を含む、または
- 4 桁の数字のシーケンスが含まれていますが、数字のシーケンスではなくなりました (個別にも)。
たとえば、(1) では が表示されます1234a56789
が、(2) では は表示されません。
それ自体がそれ以上長い数字のシーケンスの一部ではない 4 桁の数字のシーケンスを含むすべての行を表示する場合、1 つの方法は次のとおりです。
grep -P '(?<!\d)\d{4}(?!\d)' file
これはPerl 正規表現Ubuntuのgrep
(GNU グレップ) は を介してサポートされます-P
。 のようなテキストには一致しませんし、その一部であるや12345
にも一致しません。1234
2345
しかし、それは と一致し1234
ます1234a56789
。
Perl 正規表現の場合:
\d
[0-9]
任意の数字を意味します (または の短縮形です[[:digit:]]
)。x{4}
マッチx
4 回。({
}
構文は Perl 正規表現に固有のものではなく、 を介した拡張正規表現にgrep -E
もあります。) したがって、 は\d{4}
と同じです\d\d\d\d
。(?<!\d)
はゼロ幅の否定的な後読みアサーションです。これは「 が先行しない限り\d
」を意味します。(?!\d)
はゼロ幅の否定先読みアサーションです。これは「 が続かない限り\d
」を意味します。
(?<!\d)
4 桁の数字のシーケンスの外側のテキストには(?!\d)
一致しません。代わりに、これらを (一緒に使用した場合) 使用すると、4 桁の数字のシーケンスが長い数字のシーケンスの一部である場合に、その 4 桁の数字のシーケンス自体が一致しないようにします。
後読みだけ、または先読みだけを使用するのは不十分です。右端または左端の 4 桁の部分シーケンスが依然として一致してしまうためです。
使用することの利点の1つは後読みアサーションと先読みアサーションパターンは 4 桁の数字のシーケンス自体にのみ一致し、周囲のテキストには一致しません。これは、色の強調表示 (オプションを使用--color
) を使用する場合に役立ちます。
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
デフォルトではUbuntuでは、各ユーザーalias grep='grep --color=auto'
が~.bashrc
ファイル. で始まる簡単なコマンドを実行すると、自動的に色が強調表示されますgrep
(これはエイリアス拡大されます)と標準出力は端末(これは何--color=auto
一致したものは通常、赤色(朱色)ですが、斜体の太字で表示しています。スクリーンショットは次のとおりです:
また、grep
次のようにすると、行全体ではなく、一致するテキストのみを印刷することもできます-o
。
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
別の方法、それなし後読みアサーションと先読みアサーション
ただし、次の場合は:
grep
Perl正規表現をサポートしていない-P
、またはPerl正規表現を使用したくないシステムでも実行できるコマンドが必要です。そして- 4桁の数字を具体的に一致させる必要はありません。これは、一致した行を表示するだけであればよくあるケースです。そして
- 少し洗練されていない解決策でも構わない
...これを実現するには拡張正規表現その代わり:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
これは、4 つの数字と、それらを囲む数字以外の文字 (または行の先頭または末尾) に一致します。具体的には、次のようになります。
[0-9]
は任意の数字 ( Perl 正規表現の[[:digit:]]
、 または など) に一致し、 「4 回」を意味します。つまり、4 桁の数字のシーケンスに一致します。\d
{4}
[0-9]{4}
[^0-9]
0
からの範囲外の文字と一致します。これは、 (またはPerl 正規表現の )9
と同等です。[^[:digit:]]
\D
^
括弧内にない場合は[
]
、行の先頭に一致します。同様に、$
行の末尾に一致します。|
手段または括弧はグループ化のために使用されます (代数の場合と同様)。したがって、(^|[^0-9])
は行の先頭または数字以外の文字に一致し、($|[^0-9])
は行の末尾または数字以外の文字に一致します。
したがって、一致は、次の 4 桁のシーケンス ( [0-9]{4}
) が同時に含まれる行でのみ発生します。
- 行頭または数字以外の文字(
(^|[^0-9])
)の後に続く場合、そして - 行末または数字以外の文字 (
($|[^0-9])
) が続きます。
一方、4桁の数字のシーケンスを含むが、どれでも4 桁を超える数字のシーケンス (4 桁のみの別のシーケンスとは別のシーケンスであっても) の場合、概念的には、1 つのパターンに一致し、別のパターンには一致しない行を見つけることが目標になります。
したがって、単一のパターンでそれを行う方法を知っていても、次のようなものを使用することをお勧めします。マットの2 番目の提案は、grep
2 つのパターンを個別に処理することです。
そうすることでPerl正規表現の高度な機能の恩恵をあまり受けないので、使わない方が良いかもしれません。しかし、上記のスタイルに合わせるために、以下はマットの解決策\d
の代わりに (および中括弧)を使用します[0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
を使用しているため[0-9]
、マットのやり方grep
はより移植性が高く、 Perl 正規表現をサポートしていないシステムでも動作します。の代わりに[0-9]
(または) を使用し、 を引き続き使用すると、matt の方法の移植性がもう少し簡潔になります。[[:digit:]]
\d
{
}
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
代替方法、単一パターンを使用
もし本当にgrep
コマンドを好むのであれば
- 単一の正規表現を使用する(2つの
grep
sをaで区切らないパイプ、 上記のように) - 少なくとも1つの4桁の数字のシーケンスを含む行を表示するには、
- ただし、5桁以上の数字の連続は不可。
- 数字だけでなく行全体を一致させることを気にしない(おそらくこれは気にしないでしょう)
...次のように使用できます:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
この-x
フラグは、grep
行全体が一致する行のみを表示します(行の一部ではなく)含む試合)。
この場合、 と の簡潔さによって明瞭性が大幅に向上すると考えられるため、Perl 正規表現を使用しました。ただし、 がサポートされていないシステムに移植可能なものが必要な場合は\d
、をおよび(またはおよび)に置き換えることができます。\D
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)*
orは、4桁以下の数字の後に数字以外の文字が続く([0-9]{0,4}[^0-9])*
0回以上の(*
)例に一致します。数字が0(つまり何もない)は、「4桁以下の数字」の1つの可能性です。これは、(ア)空の文字列または(ロ)任意の文字列エンディング数字以外の文字で、4 桁を超える数字のシーケンスは含まれません。\d{4}
中央の(または)のすぐ左のテキストは[0-9]{4}
空であるか、数字以外で終わる必要があるため、中央の\d{4}
4 つの数字のすぐ左に別の (5 番目の) 数字がある 4 つの数字と一致しなくなります。右側の
(\D\d{0,4})*
orは、([^0-9][0-9]{0,4})*
0回以上の(*
)非数字の後に4桁以下の数字が続くもの(前述のように、4桁、3桁、2桁、1桁、または0桁の場合もあります)に一致します。これは(ア)空の文字列または(ロ)任意の文字列始まり数字以外の文字で、4 桁を超える数字のシーケンスは含まれません。中央の
\d{4}
(または[0-9]{4}
) のすぐ右側のテキストは空であるか、数字以外で始まる必要があるため、中央の\d{4}
4 つの数字のすぐ右側に別の (5 番目の) 数字がある場合、中央の 4 つの数字が一致するのを防ぎます。
これにより、4 桁のシーケンスがどこかに存在し、5 桁以上のシーケンスがどこにも存在しないことが保証されます。
この方法が悪いとか間違っているというわけではありません。しかし、おそらくこの代替案を検討する最も重要な理由は、上記およびgrep -P '\d{4}' file | grep -Pv '\d{5}'
マットの答え。
この方法なら、あるものを含み、別のものは含まない行を選択することが目的であることは明らかです。さらに、構文はよりシンプルです (そのため、多くの読者やメンテナーがより早く理解できる可能性があります)。
答え2
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]
答え3
file
システム内の実際のファイル名に置き換えて、以下のコマンドを試すことができます。
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
確認することもできますこのチュートリアルgrep コマンドのその他の使用法については、こちらをご覧ください。
答え4
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