블록 장치에서 정규식 발생 찾기(라인 길이 버퍼 문제)

블록 장치에서 정규식 발생 찾기(라인 길이 버퍼 문제)

#문자로 시작 하고 1635700000에서 1653699999 사이에 있고 널 문자( \0) 또는 Linux 개행 문자( )로 \0xA끝나는 두 블록 장치의 모든 숫자를 찾으려고 합니다 .

내가 생각해낸 것은 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

이는 블록 장치 중 하나에서 작동하지만 다른 장치에서는 일부 출력이 아닌 일부 출력 후에 오류와 함께 중지됩니다.

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

두 번째 블록 장치에서 찾고 있는 것을 어떻게 찾나요? 더 큰 입력 버퍼를 사용하거나 전체 줄을 읽지 않는 다른 유틸리티이거나 맞춤형 perl/python/C/C++ 프로그램일 수도 있습니다.

발견된 바이트 오프셋과 숫자를 포함하여 발견된 각 일치 항목에 대해 한 줄을 출력해야 합니다.

블록 장치 수정은 옵션이 아닙니다. 수만 개의 결과가 있으므로 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자리, 마지막으로 줄 끝 또는 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

관련 정보