grepが期待どおりに機能しない

grepが期待どおりに機能しない

次の内容を含む「test.log」ファイルが提供されます。

line1 Patient 123 45566
line2 Patient 432
line3 Patient 234 456
line4 Patient 321
line5

line 2次のパターンを選択して使用しようとしていますline 4

grep "Patient\s\d+\s" test.log
# but this works testing at https://rubular.com/

うまくいかず、次も行われません。

grep "Patient\s\d+\n" test.log
# but this works testing at https://regexr.com/47qd5

私は何が間違っていましたか?

答え1

1. 名前付きクラスまたは PCRE を使用します。

GNUはgrepデフォルトでデフォルト正規表現(BRE)を使用しますが、拡張正規表現(ERE)とPerl互換正規表現(PCRE)も使用できます。

BRE と ERE はどちらもサポートされていませ\sんが、\d同様の機能があります。からman grep

最後に、以下のように、特定の名前付き文字クラスが角かっこ式内で事前定義されています。その名前は説明を必要としません。つまり[:alnum:]、、、、、、、、、、、、およびです[:alpha:]。​​​​​​​たとえば、現在のロケールの数字と文字を表す文字クラスです。 CロケールとASCII文字セットエンコーディングでは、これは同じです。 (これらのクラス名の角かっこは記号名の一部であり、角かっこ式を区切る角かっこで含める必要があります。)ほとんどのメタ文字はその文字を失います。角かっこ式内の特別な意味。テキストを含めるには、リストの最初に入れます。同様に、テキストを含めるには、始めを除く任意の場所に配置します。最後に、テキストを最後に置きます。[:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:][[:alnum:]][0-9A-Za-z]]^-

例:

$ grep -E '^[[:digit:]]+$' << 'EOF'
> foo
> 123
> bar
> EOF
123

\s以下をサポートするPCREを使用することもできます\d

$ grep -P '^\d+$' << 'EOF'
> foo
> 123
> bar
> EOF
123

2.\n動作しません

Unixではそれぞれ\nが区切りになります。ワイヤーgrep印刷ワイヤー与えられたパターンと一致します。この場合、一致\n自体は意味がありません。

$以下を使用して行末を一致させることができます。

$ grep -E 'foo bar$' << 'EOF'
> foo
> foo bar
> foo bar baz
> EOF
foo bar

または、 -z/--null-dataオプションを渡して「複数行」モードを有効にします(必要なものと正確に一致するには、いくつかの追加の解決策が必要です)。

$ grep -Poz '(?<=\n)?foo bar\n' << 'EOF'
> foo
> foo bar
> foo bar baz
> EOF
foo bar

3. 最初の例はあなたの考えに合わない。

最後のものはandの代わりにandと\s一致します:line 1line 3line 2line 4

$ grep -P 'Patient\s\d+\s' << 'EOF'
> line1 Patient 123 45566
> line2 Patient 432
> line3 Patient 234 456
> line4 Patient 321
> line5
> EOF
line1 Patient 123 45566
line3 Patient 234 456

答え2

-PPerl正規表現にGNU grepでスイッチを使用すると、構文は必要に応じて機能します。

$ grep -V | head -n1
grep (GNU grep) 2.25

$ grep --help | grep "\-P"
  -P, --perl-regexp       PATTERN is a Perl regular expression

また見てくださいこの回答より多くの情報を知りたいです。

答え3

他の人がすでに指摘したように、すべての正規表現が同じ表記法を使用するわけではありません。基本的なgrep実装がGNU以外のシステムを使用している場合は、grepPOSIX正規表現があり、Perlに似たパターン(たとえば)を使用しないことになります\s

grep(ゼロ以上の整数ではなく)単一の正の整数で終わる行が欲しいようです。データを見ると、スペースで区切られた3つのフィールドのみを含むすべての行を抽出するもう1つの方法です。

それは簡単ですawk

$ awk 'NF == 3' test.log
line2 Patient 432
line4 Patient 321

NFは現在のレコード(行)のフィールド(列)の数で、条件行は1つだけです。基本的な作業は、条件を満たすすべての行を印刷することです。

grep、およびより完全なパターンを使用して私達が期待するものを正確に指定しなさい。

$ grep -Ex '[[:alnum:]]+ [[:alpha:]]+ [[:digit:]]+' test.log
line2 Patient 432
line4 Patient 321

-E拡張正規表現を有効にし(拡張+修飾子を使用するため)、行全体が一致するようにします-xgrep

[[:alnum:]]+ロケールに応じて文字と数字と一致し、および[[:alpha:]]+[[:digit:]]+それぞれ文字と数字の文字列と一致します。

ASCII範囲を使用して同じ内容を作成する別の方法(ロケールを無視):

grep -Ex '[A-Za-z0-9]+ [A-Za-z]+ [0-9]+' test.log

答え4

古いMacOSで実行されていたgrepバージョンはgrep (BSD grep) 2.5.1-FreeBSDサポートされていない-Pため、3.3をインストールしbrew install grep --with-default-namesて動作させることができました。

grep -P 'Patient\s\d+$' test.log

関連情報