파일에서 바이트 시퀀스가 ​​발생하는 횟수를 어떻게 계산할 수 있습니까?


16

내가 가지고있는 파일 내에서 특정 바이트 시퀀스가 ​​몇 번 발생하는지 계산하고 싶습니다. 예를 들어, \0xdeadbeef실행 파일 내 에서 숫자 가 몇 번이나 발생 하는지 알고 싶습니다 . 지금은 grep을 사용하여 수행하고 있습니다.

#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file

(내 CPU는 리틀 엔디안이기 때문에 바이트는 역순으로 기록됩니다)

그러나 접근 방식에는 두 가지 문제가 있습니다.

  • \Xnn이스케이프 시퀀스는 물고기 쉘에서 작동합니다.
  • grep은 실제로 내 마법 번호를 포함하는 줄 수를 계산합니다. 패턴이 같은 줄에서 두 번 발생하면 한 번만 계산됩니다.

이러한 문제를 해결하는 방법이 있습니까? 이 하나의 라이너를 Bash 쉘에서 실행하고 패턴이 파일 내에서 발생하는 횟수를 정확하게 계산하려면 어떻게해야합니까?


약간의 도움 : unix.stackexchange.com/q/231213/117549- 구체적으로,grep -o
Jeff Schaller

1
grep은 잘못된 도구입니다. bgrep 또는 bgrep2를 고려하십시오.
fpmurphy

3
검색 할 시퀀스가이면 다음 11221122과 같은 입력에서 무엇을 반환해야 112211221122합니까? 1 또는 2?
Stéphane Chazelas

이 경우 2 또는 3 경기를보고해도 괜찮습니다. 구현하기가 더 쉬운 것입니다.
hugomg

답변:


15

이것은 요청 된 한 줄짜리 솔루션입니다 ( "프로세스 대체"가있는 최근 쉘).

grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l

"프로세스 대체" <(…)가 없으면 grep을 필터로 사용하십시오.

hexdump -v -e '/1 "%02x "' infile.bin  | grep -o "ef be ad de" | wc -l

아래는 솔루션의 각 부분에 대한 자세한 설명입니다.

16 진수의 바이트 값 :

첫 번째 문제는 해결하기 쉽습니다.

이 \ Xnn 이스케이프 시퀀스는 생선 껍질에서만 작동합니다.

상단 X을 하단으로 변경하고 xprintf (대부분의 쉘)를 사용하십시오.

$ printf -- '\xef\xbe\xad\xde'

또는 사용하십시오 :

$ /usr/bin/printf -- '\xef\xbe\xad\xde'

'\ x'표현을 구현하지 않기로 선택한 쉘의 경우.

물론 16 진수를 8 진수로 변환하면 거의 모든 쉘에서 작동합니다.

$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'

여기서 "$ sh"는 (합리적인) 쉘입니다. 그러나 올바르게 인용하는 것은 매우 어렵습니다.

이진 파일.

가장 강력한 솔루션은 파일과 바이트 시퀀스 (둘 다)를 (new line) 0x0A또는 (null byte) 와 같은 홀수 문자 값에 문제가없는 인코딩으로 변환하는 것 0x00입니다. 둘 다 "텍스트 파일"을 처리하도록 설계된 도구를 사용하여 올바르게 관리하기가 매우 어렵습니다.

base64와 같은 변환은 유효한 것으로 보이지만 모든 입력 바이트가 mod 24 (비트) 위치의 첫 번째, 두 번째 또는 세 번째 바이트인지에 따라 최대 3 개의 출력 표현을 가질 수있는 문제를 나타냅니다.

$ echo "abc" | base64
YWJjCg==

$ echo "-abc" | base64
LWFiYwo=

$ echo "--abc" | base64
LS1hYmMK

$ echo "---abc" | base64        # Note that YWJj repeats.
LS0tYWJjCg==

16 진 변환

따라서 가장 강력한 변환은 간단한 HEX 표현과 같이 각 바이트 경계에서 시작하는 변환이어야합니다.
이 도구 중 하나를 사용하여 파일의 16 진 표현으로 파일을 얻을 수 있습니다.

$ od -vAn -tx1 infile.bin | tr -d '\n'   > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin  > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' '    > infile.hex

이 경우 검색 할 바이트 시퀀스는 이미 16 진입니다.
:

$ var="ef be ad de"

그러나 변형 될 수도 있습니다. 왕복 hex-bin-hex의 예는 다음과 같습니다.

$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de

검색 문자열은 이진 표현으로 설정 될 수 있습니다. 위에서 제시 한 od, hexdump 또는 xxd의 세 가지 옵션 중 하나가 동일합니다. 일치가 바이트 경계에 있도록 공백을 포함하십시오 (니블 시프트가 허용되지 않음).

$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de

이진 파일이 다음과 같은 경우 :

$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074  This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70  est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120  ut ......from a 
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131  bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000060: 3232 0a

그런 다음 간단한 grep 검색은 일치하는 시퀀스 목록을 제공합니다.

$ grep -o "$a" infile.hex | wc -l
2

한 줄?

모두 한 줄로 수행 할 수 있습니다.

$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l

예를 들어, 11221122동일한 파일에서 검색 하려면 다음 두 단계가 필요합니다.

$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4

일치하는 것을 "보려면":

$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232

$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')

… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a


버퍼링

grep이 전체 파일을 버퍼링하고 파일이 큰 경우 컴퓨터에 많은 부담을 줄 우려가 있습니다. 이를 위해 버퍼되지 않은 sed 솔루션을 사용할 수 있습니다.

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  | 
    sed -ue 's/\('"$a"'\)/\n\1\n/g' | 
        sed -n '/^'"$a"'$/p' |
            wc -l

첫 번째 sed는 버퍼되지 -u않고 ( ) 일치하는 문자열 당 스트림에 줄 바꿈을 두 개만 주입하는 데만 사용됩니다. 두 번째 sed는 일치하는 줄만 인쇄합니다. wc -l은 일치하는 행을 계산합니다.

짧은 줄만 버퍼링합니다. 두 번째 sed에서 일치하는 문자열입니다. 사용되는 리소스가 매우 적어야합니다.

또는 이해하기가 다소 복잡하지만 하나의 sed에서 동일한 아이디어가 있습니다.

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  |
    sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
        wc -l

2
모든 텍스트를 한 줄에 넣으면 grep메모리에 전체를로드하게됩니다 (여기서는 16 진수 인코딩으로 인해 원본 파일 크기의 두 배 + 1). 결국에는 더 많이 나타납니다 오버 헤드보다 python접근 또는 perl하나 -0777. 또한 grep임의 길이의 행을 지원 하는 구현이 필요합니다 ( -o일반적으로 지원하는 행). 그렇지 않으면 좋은 대답입니다.
Stéphane Chazelas

1
16 진 버전은 니블 시프트 값과 일치합니까? e fb ea dd e? 원하는 바이트 외에. od -An -tx1 | tr -d '\n'또는 hexdump -v -e '/1 " %02x"'공백을 포함하는 검색 문자열을 사용하면 이것을 피할 수 있지만에 대한 수정은 없습니다 xxd.
dave_thompson_085

@ dave_thompson_085 답변이 수정되었습니다. 나는 대답이 바이트 경계와 만 일치한다고 믿습니다. 다시 감사합니다.
sorontar

@ StéphaneChazelas 버퍼되지 않은 sed를 사용하는 제안 된 옵션을 검토 할 수 있습니다. 감사.
sorontar

sed -u(가능한 경우) 버퍼 해제 용입니다. 즉, 입력시 한 번에 1 바이트를 읽고 버퍼링하지 않고 출력을 바로 출력합니다. 어쨌든 여전히 패턴 공간에 전체 라인을로드해야하므로 여기에서는 도움이되지 않습니다.
Stéphane Chazelas

7

GNU grep-P(perl-regexp) 플래그로

LC_ALL=C grep -oaP '\xef\xbe\xad\xde' file | wc -l

LC_ALL=C바이트 grep순서를 문자로 해석하려고하는 멀티 바이트 로케일의 문제점을 피하는 것입니다.

-a이진 파일을 텍스트 파일과 동등한 것으로 취급합니다 (정상적인 동작 대신, grep하나 이상의 일치 여부 만 인쇄)


이 솔루션은 항상 정확한 숫자 대신 0 개의 일치 항목을 제공합니다.
hugomg

@hugomg, 전달하기 grep 위해 전달 된 바이트를 반대로해야 합니까?
iruvar

나는 그것이 순서라고 생각하지 않습니다. 이 질문에 대한 다른 두 가지 답변이 올바르게 작동합니다.
hugomg

2
@hugomg, 로케일입니다. 편집을 참조하십시오.
Stéphane Chazelas

2
-a옵션 을 포함하는 것이 좋습니다 . 그렇지 않으면 grep이 Binary file file.bin matches이진으로 탐지 한 파일에 대해 grep이 응답 합니다.
sorontar

6
PERLIO=:raw perl -nE '$c++ while m/\xef\xbe\xad\xde/g; END{say $c}' file

어떤 취급 이진 입력 파일 (들) (줄 바꿈 또는 인코딩 없음 번역을 참조 perlrun 다음 입력 파일 (들) 지정된 진수의 모든 일치에 대한 카운터를 증가 인쇄되지 않는 이상 루프 (또는 어떤 형태 참조) perlre를 ) .


2
검색 할 시퀀스에 바이트 0xa가 포함되어 있으면이를 사용할 수 없습니다. 이 경우 다른 레코드 구분 기호 ( -0ooo)를 사용할 수 있습니다 .
Stéphane Chazelas

1
@ StéphaneChazelas 당신은 $/약간 다른 트레이드 오프 (그러한 시퀀스 사이의 최대 거리에 비례하는 메모리 사용)와 함께 관심 시퀀스 자체를 사용할 수 있습니다 :perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
hobbs

@ StéphaneChazelas 바이트 값에 대한 솔루션에 대한 내 대답을 읽으십시오.
sorontar

1
@hobbs, 어쨌든 메모리 사용은 텍스트가 아닌 파일의 경우 임의로 큰 두 개의 0xa 바이트 사이의 최대 거리에 비례합니다.
Stéphane Chazelas

5

GNU awk를 사용하면 다음을 수행 할 수 있습니다.

LC_ALL=C awk -v 'RS=\xef\xbe\xad\xde' 'END{print NR - (NR && RT == "")}'

바이트 중 하나가 ERE 연산자 인 경우 (with \\) 를 통해 이스케이프해야합니다 . 마찬가지로 0x2e이는이 .로 입력해야 \\.하거나 \\\x2e. 그 외에는 0과 0xa를 포함한 임의의 바이트 값으로 작동해야합니다.

NR-1몇 가지 특별한 경우가 있기 때문에 간단하지 않습니다 .

  • 입력이 비어 있으면 NR은 0이고 NR-1은 -1을 나타냅니다.
  • 레코드 구분 기호에서 입력이 끝나면 그 후에 빈 레코드가 작성되지 않습니다. 우리는로 테스트합니다 RT=="".

또한 최악의 경우 (파일에 검색어가 포함되어 있지 않은 경우 파일이 전체 메모리에로드 됨)주의하십시오.


5

가장 간단한 번역은 다음과 같습니다.

$ echo $'\xef\xbe\xad\xde' > hugohex
$ echo $'\xef\xbe\xad\xde\xef\xbe\xad\xde' >> hugohex
$ grep -F -a -o -e $'\xef\xbe\xad\xde' hugohex|wc -l
3

내가 사용했던 경우 $'\xef'는 AS ANSI는 인용 부호 bash는 (원래 ksh93기능, 지금 지원 zsh, bash, mksh, FreeBSD의 sh) 물고기의 버전 \Xef및 사용 grep -o ... | wc -l인스턴스를 계산. grep -o각 일치 항목을 별도의 줄에 출력합니다. 이 -a플래그는 grep이 텍스트 파일에서와 동일한 방식으로 이진 파일에서 작동하도록합니다. -F고정 문자열 용이므로 정규 표현식 연산자를 이스케이프 처리 할 필요가 없습니다.

귀하의 fish경우 와 마찬가지로 , 검색 할 시퀀스에 바이트 0 또는 0xa (ASCII의 줄 바꿈)가 포함되어 있으면이 방법을 사용할 수 없습니다.


사용 printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'대부분의 휴대용 "순수 쉘"방법이 될 것입니다. 물론 printf "efbeadde" | xxd -p -r > hugohex가장 실용적인 방법 인 것 같습니다.
sorontar

4

파이썬의 bytes.count메소드를 사용 하여 바이트 문자열에서 겹치지 않는 하위 문자열의 총 수를 얻을 수 있습니다 .

python -c "print(open('./myexecutable', 'rb').read().count(b'\xef\xbe\xad\xde'))"

이 하나의 라이너는 전체 파일을 메모리에로드하므로 가장 효율적이지 않지만 작동하고 Perl보다 더 읽기 쉽습니다 .D


'Perl보다 더 읽기 쉽다'는 TECO의 한 걸음입니다. IINM은 다음과 같습니다. 239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=(gd & r)
dave_thompson_085

mmap()파이썬으로 파일을 만들 수 있습니다 . 메모리 커밋이 줄어 듭니다.
Toby Speight

1
tr "$(printf \\0xef)\n" \\n\\0 < infile |
grep -c "^$(printf "\0xbe\0xad\0xde")"

1

나는 당신이 Perl을 사용할 수 있다고 생각합니다.

perl -0777ne 'CORE::say STDOUT s/\xef\xbe\xad\xde//g' file_name  

명령을 교체 s했다 대체의 수를 제공, -0777 수단 특수 문자로하지 대접 새로운 라인을, e- 명령을 실행 say한 후, 새로운 라인 문자를 인쇄 할 다음에 어떤 일이 일어나는지 인쇄하기 n내가 완전히 파악하지 않았다, 그러나 일을하지 않는 w / 아웃 -에서 문서 :

Perl이 프로그램 주위에서 다음 루프를 가정하게하여 sed -n 또는 awk와 같은 파일 이름 인수를 반복합니다. LINE : while (<>) {... # 프로그램은 여기에}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.