LinuxでgrepがMACアドレスとホスト名を見つけることができないのはなぜですか?

LinuxでgrepがMACアドレスとホスト名を見つけることができないのはなぜですか?

mac-hostsMACアドレスと関連ホスト名を含むファイルがあります。

e4:5f:01:21:79:01 PF3
e4:5f:01:21:79:03 PF3-BR0
e4:5f:01:21:79:be PF2
e4:5f:01:21:79:c0 PF2-BR0

正しい形式のMACアドレスとホスト名を持つ行数を数えるには、次の式を使用します。

FILTERED=$(cat mac-hosts | grep -P -c '/^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?$/i')

この表現のすべてのバージョンで私はFILTERED = 0結果を得ます。

確認しましたhttps://regex101.com/ファイル内のすべての行は、mac-hosts逆参照が意味のないGoLangとRustを除いて、提供されているすべてのバージョンでエラーや警告なしにフィルタ式と正しく一致します。このmanページも調査しましたが、grepフィルタが機能しない理由が見つかりませんでした。

-Pそうでない場合は、grep: Invalid back referencePerl互換の正規表現構文を使用していることがわかります。

私はこの欠陥が最新バージョンのLinuxを実行しているRaspberry Pi 4Bで発生したことを初めて発見しました。

pi@PF2:~ $ uname -a
Linux PF2 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux

pi@PF2:~ $ grep -V
grep (GNU grep) 3.6
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others; see
<https://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

git-bashその後、Windows 10で実行すると同じ動作が観察されました。

この問題をどのようにデバッグし、予想される結果を得ることができますかFILTERED = 4?結果はどこにありますか?


修正する

i返信ありがとうございます。答えを見たときは明らかでした。文字列の一部と一致しないスラッシュを区切る必要がある状況と、「大文字と小文字を無視する」フラグを考えていました。コマンドラインでは、区切りgrep文字は使用されず、「大文字と小文字を無視」はスイッチに設定されます-i

FILTERED=$(grep -Pic '^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?$' mac-hosts)

アップデート2

ホスト名にまだ問題があります。いいえ2番目の部分(ハイフンと他のalm)があります。この名前の最後には、(驚くべきことに)画面に表示されないスペースがあることがわかりました。末尾のスペースを見つけるために一致文字列に別のコンポーネントを追加しました。これで最終テストは正常に動作します。

FILTERED=$(grep -Pic '^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?[[:space:]]$' mac-hosts)

作成者が行末からテストを削除した提案された編集内容をロールバックしました。ただし、許可されている誤った行はフィルタリングされません。たとえば、ホスト名の後の句読点はこの形式では許可されません。

ベストアンサー1

grepがファイルの行と一致しない理由は、一見すると、正規表現でファイルの行に表示されない先行スラッシュ(/)文字と末尾の文字()があるため、/i一致する行です。 Perlスクリプト/の正規表現の始まりと終わり(および末尾のスラッシュの後の修飾子)は、正規表現自体の一部ではなく、正規表現に関連する区切り文字と修飾子です。

私は2つを提案します:

  1. POSIX 文字クラスを使用して、MAC アドレスとホスト名に一致するより単純な式を作成します。
  2. grep-cオプションなしでコマンドラインでコマンドをテストし、正規表現が正しい行と一致するまでターミナルウィンドウに出力を送信します。正しい場合は、-cを追加して数を確認してください。テストが成功した後にのみ、スクリプトの変数に出力をキャプチャします。

このコマンドは、Ubuntu 20.04システムにインストールされているGNU grepを使用して機能します。

grep -E '^[[:xdigit:]:]+ +[[:alnum:]_-]+' mac-hosts

ファイルを呼び出すことも、パイピングをすることなくgrepファイルから直接読み取ることもできます。catgrep

この拡張正規表現(-Eオプションで有効)は、空白文字で区切られた印刷可能文字の2つの「フィールド」と一致します。最初のフィールドは行の先頭にあり、1つ以上の16進文字またはコロン(:)文字で構成されています。 2番目のフィールドは、1つ以上の英数字、アンダースコア(_)、またはダッシュ(-)文字で構成されています。

これは、最初のフィールドにコロンで区切られた2つの16進文字で構成される正確に6つのフィールドを強制せず、2番目のフィールドに英数字(または大文字のみ)なしでダッシュまたはアンダースコアのみを表示することを強制しません。新しいユースケースに合わせて理解してカスタマイズするのははるかに簡単です。

上記の方法を使用して、大幅に誤ったファイル行(ホスト名の欠落など)をフィルタリングし、次に「良好な」行のフィールドに対してより厳密な構文チェックを実行するサブルーチンを作成します。気になったら、そうです。私のスクリプトでは、追加の構文チェックサブルーチンを作成しなくても、上記の内容で作業を完了できます。

一致する必要がある行に一致するように正規表現を調整した後、-cオプションを追加して、行自体ではなく一致する行の数を出力できます。 -E regexオプションをコマンドラインの式と共に使用するよりgrep -c -Eも、オプションと引数の順序を指定する方がよいでしょう。grep -E -c技術的には不要ですが、このような小さなものは、スクリプトを読んでいる人がスクリプトを理解/更新するのに役立ちます。 (通常数ヶ月後です。)

Perl式を好む場合は、-Eの代わりに-Pを使用してコマンドを実行できます。

おすすめ記事