ブロックデバイスで正規表現発生を検索する(ライン長バッファの問題)

ブロックデバイスで正規表現発生を検索する(ライン長バッファの問題)

#文字で始まり、1635700000から1653699999の間にあり、NULL文字(\0)またはLinux改行文字()で\0xA終わる2つのブロックデバイスのすべての数字を見つけようとします。

私が思いついたのはgrep確かにエレガントではありません。

grep --only-matching --byte-offset --text -Pa '#1635[7-9][0-9]{5}(\x0|$)|#163[6-9][0-9]{6}(\x0|$)|#164[0-9]{7}(\x0|$)|#165[0-2][0-9]{6}(\x0|$)|#1653[0-6][0-9]{5}(\x0|$)' /dev/device

このように入力して実行することはできませんが、読みやすくするためにいくつかの改行を含む同じステートメントがあります。

grep --only-matching --byte-offset --text -Pa '
 #1635[7-9][0-9]{5}(\x0|$)
|#163[6-9][0-9]{6}(\x0|$)
|#164[0-9]{7}(\x0|$)
|#165[0-2][0-9]{6}(\x0|$)
|#1653[0-6][0-9]{5}(\x0|$)
' /dev/device

これはブロックデバイスの1つで動作しますが、他のデバイスでは一部の出力ではなく、一部の出力の後にエラーで停止します。

grep: exceeded PCRE's line length limit

\0故障したブロックデバイスに長いバイトがあり、文字がないか、行長制限\0xAしきい値を超えたようです。

だからNULL文字を改行文字に変更してみました。

sed 's/\x0/\n/g' /dev/device | grep ...

しかし、ほぼ同じ理由で停止します。

sed: regex input buffer length larger than INT_MAX

2番目のブロックデバイスで探しているものをどのように見つけますか?より大きな入力バッファを使用したり、行全体を読み取ったりしない他のユーティリティ、またはカスタムperl / python / C / C ++プログラムです。

見つかったバイトオフセットと数字を含む、見つかった一致ごとに1行を出力する必要があります。

ブロックデバイスの変更はオプションではありません。数万の結果があるため、16進エディタなどのツールで手動で検索することもオプションではありません。

答え1

上記のコメントでは、@terdonは最初に検索スペースを減らすための重要な洞察を提供しました。 Perl(PCRE)grepパターン構文の最大行長を減らすために、拡張grepパターン構文を使用して機能させることができました。

grep --only-matching --byte-offset --text -E '#[0-9]{10}.' /dev/device | grep --only-matching --text -P '[0-9]*:#1635[7-9][0-9]{5}(\x0|$)|[0-9]*:#163[6-9][0-9]{6}(\x0|$)|[0-9]*:#164[0-9]{7}(\x0|$)|[0-9]*:#165[0-2][0-9]{6}(\x0|$)|[0-9]*:#1653[0-6][0-9]{5}(\x0|$)' /dev/device

このように入力して実行することはできませんが、読みやすくするためにいくつかの改行を含む同じステートメントがあります。

grep --only-matching --byte-offset --text -E 
   '#[0-9]{10}.'
   /dev/device
| grep --only-matching --text -P '
    [0-9]*:#1635[7-9][0-9]{5}(\x0|$)
   |[0-9]*:#163[6-9][0-9]{6}(\x0|$)
   |[0-9]*:#164[0-9]{7}(\x0|$)
   |[0-9]*:#165[0-2][0-9]{6}(\x0|$)
   |[0-9]*:#1653[0-6][0-9]{5}(\x0|$)
   ' /dev/device

拡張grepパターン構文エンジンには、私が経験した行の長さ制限はなく、Perl(PCRE)パターン構文エンジンに提供される最大行長が短くなります。

答え2

bash(1)、grep(1) および perl(1) を使用したソリューションは次のとおりです。

 1  #!/bin/bash
 2  grep -P -a -b -o '#\d{10}(\000|$)' \
 3  | perl -ne '/(\d{10})/; print if 1635700000 <= $1 && $1 <= 1653699999' \
 4  | perl -pe 'chop; /\000/ ? do {chop; $_ .= "\\000\n"} : do {$_ .= "\\n\n"}'

行1は、これがBashスクリプトであることをシェルに伝えます。

行2〜4はコマンドパイプラインを構成します。

2行目はgrep(1)を呼び出します:

  • 「-P」オプションは、パターンをPerl準拠の正規表現として解釈する必要があることを指定します。

  • オプション "-a" は、バイナリ入力をテキストとして扱う必要があることを指定します。

  • 「-b」オプションと「-o」の組み合わせは、一致する部分のバイトオフセットを各出力行の前に印刷する必要があることを指定します。

  • "-o"オプションは、一致する部分のみを印刷するように指定します。

  • パラメータ "#\d{10}($|\000)" は、数字記号、10 進数 10 桁、最後に行末、または NUL で構成される正規表現パターンです。 Unix / Linuxでは、正規表現行の終わり "$"メタ文字はASCIIキャリッジリターン文字(問題の説明 "\ 0xA")と一致します。正規表現は、他の改行エンコード(MS-DOSのCR-LF、クラシックMac OSのLFなど)を使用しているプラ​​ットフォームで実行されている場合、ASCIIキャリッジリターンを見つけることができない可能性があります。

ライン 3 は、数値部分が所望の範囲内にあるラインのみを通過する Perl 断線フィルタです。

Line 4 は、終了行改行または NUL を表示する Perl シングルライナーです。

実行例は次のとおりです。

52:#1647787407\n
70:#1644931194\n
84:#1651134631\000
154:#1646920743\n

関連情報