내용에서 100 % NUL 문자를 가진 파일을 찾는 방법은 무엇입니까?


16

그러한 파일을 식별 할 수있는 Linux 명령 행 명령은 무엇입니까?

AFAIK find명령 (또는 grep)은 텍스트 파일 내의 특정 문자열 과 만 일치 할 수 있습니다 . 하지만 즉, 내가 정규 표현식과 일치하는 파일을보고 싶은, 전체 내용과 일치 할 \0+, 라인 끝 문자 (들)을 무시 . 어쩌면 find . cat | grep관용구가 작동 할 수는 있지만 grep 무시 라인을 만드는 방법을 모르고 파일을 이진 파일로 취급합니다.

배경 : 랩탑이 멈출 때마다 btrfs 파티션이 정보를 잃습니다. 쓰기 위해 열린 파일의 내용이 0으로 바뀝니다 (파일의 크기는 그대로 유지됩니다). 동기화를 사용하고 이러한 가짜 파일이 전파되는 것을 원하지 않습니다. 백업에서 파일을 가져올 수 있도록 식별 할 수있는 방법이 필요합니다.


숫자가 0 인 파일을 의미합니까?
— Rahul Patil

2
숫자 0이 아닌 NULL 문자라고 생각합니다.
— gertvdijk

10
한 걸음 물러나 자 며칠마다 노트북이 멈출 때? 왜 우리가 해결하기 위해 노력하지 않는 것을 여기에, 진짜 문제?
— D_Bye

2
@D_Bye는 좋은 생각,하지만 지금까지 너무 멀리 오지 않았다 : [ unix.stackexchange.com/questions/57894/...
— 아담 라이크 코우 스키에게

1
-vgrep 옵션을 고려
— 했습니까? 1-255

답변:


10

grepPerl 정규식 모드를 사용하여 ␀ 문자를 사용할 수 있습니다 .

$ echo -ne "\0\0" > nul.bin
$ echo -ne "\0x\0" > non-nul.bin
$ grep -P "[^\0]" *.bin
Binary file non-nul.bin matches

그래서 이것을 사용할 수 있습니다 :

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done

를 사용하여 예기치 않은 결과가 나타납니다 GNU grep 2.5.4. --binary-files=text또는 사용 여부에 관계없이 비어 있지 않은 모든 데이터 값에 대한 결과를 --binary-files=binary제공 true합니다 (예 : "\0\0", "\0x\0", "abcd"... 내가 사용하는 정확한 코드는 다음과 같습니다 for typ in binary text ;do for dat in '\0\0' '\0x\0' 'abcd' '' ;do printf "$dat" >f; grep --binary-files=$typ -P '[^\0]' f >/dev/null && echo true || echo false; done; done
— Peter.O

1
나는 지금 더 시도했다 GNU grep) 2.10. 이 나중 버전은 예상 된 결과를 제공합니다 ... 그래서 뒤늦은 +1
— Peter.O

1
그 문제 로 printf '\0\n\0\0\n\n' > file또는 printf '\n' > file그 문제로 만들어진 파일에서 실패합니다 .
— Stéphane Chazelas 2016 년

2
@ StéphaneChazelas OP는 "줄 끝 문자 무시하기"라고 말했습니다. 따라서 오직 문자 \0와 \n문자 로 구성되는 모든 파일 은 일치합니다.
— l0b0

6

D_Bye가 문제의 근본을 찾는 것에 대해 말한 것에 동의합니다.

어쨌든 파일에 포함되어 \0있거나 \n사용할 수 있는지 확인하려면 다음을 수행하십시오 tr.

<file tr -d '\0\n' | wc -c

null / newline 및 빈 파일에 대해 0을 반환합니다.


2
tr -d '\0\n'줄 바꿈 문제를 해결하면 출력에 빈 파일의 문제 (?) 만 남게됩니다 ... 문제는있을 수도 있고 아닐 수도 있지만 모든 파일의 모든 바이트를 처리합니다 +1
— Peter.O

@ Peter.O : 줄 바꿈 요구 사항을 놓쳤습니다. 감사합니다. 이 솔루션은 최적화되지 않았으며 많은 데이터를 실행해야한다면 일치하지 않는 바이트를 찾는 솔루션을 사용하는 것이 좋습니다.
— Thor

잘 작동합니다. 필자의 경우 길이가 0 인 파일 만 제외해야했습니다. 감사합니다.
— Adam Ryczkowski

1
그러나 이것은 또한 줄 바꿈이있는 파일을 "빈"것으로 계산합니다.
— Chris Down

1
@ChrisDown : 나는 대답 텍스트가 무엇인지 명확하게 만들었습니다. OP가 개행 전용 파일로 무엇을하고 싶은지 명확하지 않습니다.
— Thor

5

나는 그 파일이 희소하다고 생각합니다. 즉, 할당 된 디스크 공간이 없으며 파일 크기 만 지정합니다 (du 0을보고합니다).

이 경우 GNU find를 사용하면 다음과 같이 할 수 있습니다 (파일 경로에 줄 바꾸기 문자가 포함되어 있지 않다고 가정).

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-

좋은 지적. 나는 그것에 대해 생각하지 않았다. 노력하겠습니다. 를 사용 du하면 파일 시스템에있는 모든 단일 파일의 내용이 긁히지 않으므로 전체 절차를 완료하는 데 30 분 이상 걸리지 않습니다.
— Adam Ryczkowski

(그리고 printf %b위의보고 내용 du)
— Stéphane Chazelas

내가 바꿀 것 -size +0으로 -size +1제로 길이 파일이 결과에서 제외되도록. 또한 \n경로에 포함 된 파일 은이 명령에 문제를 일으킬 수 있습니다.
— 타이슨

@Tyson -size +0은 엄격하게 0보다 큰 크기 -size +1에 해당합니다. 엄격하게 512보다 큰 크기에 해당합니다. 줄 바꿈 제한은 이미 언급되었습니다.
— 스테판 Chazelas

@ StéphaneChazelas 님에 대해 알려 주셔서 감사 -size +1합니다. 내 대답을 수정했습니다 . :-)
— Tyson

4

그것을 할 수있는 작은 파이썬 프로그램이 있습니다 :

import sys

def only_contains_nulls(fobj, chunk_size=1024):
    first = True
    while True:
        data = fobj.read(chunk_size)
        if not data:
            if first:
                return 1  # No data
            else:
                return 0
        if data.strip("\0"):
            return 1
        first = False

if __name__ == '__main__':
    with open(sys.argv[1]) as f:
        sys.exit(only_contains_nulls(f))

그리고 실제로 :

$ printf '\0\0\0' > file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Only nulls
$ printf a >> file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Non-null characters

당신은 발견의 사용하여 여러 파일을 확인할 수 있습니다 -exec, xargs, GNU parallel및 이와 유사한 프로그램을. 또는 처리해야 할 파일 이름이 인쇄됩니다.

files=( file1 file2 )
for file in "${files[@]}"; do
    ./onlynulls "$file" || printf '%s\n' "$file"
done

이 결과를 다른 프로그램으로 전달하려면 파일 이름에 줄 바꿈이 포함될 수 있으므로 다르게 구분해야합니다 (와 함께 \0).

파일이 많은 경우 한 번에 하나의 파일 만 읽으므로 병렬 처리 옵션을 사용하는 것이 좋습니다.


2
(: 예를 들어, 길이 제로 파일을 조심 /etc/nologin, ~/.hushlogin, .nomedia, ...)이 대답에 의해 잘못 식별된다.
— 타이슨

@Tyson 지적 해 주셔서 감사합니다! 방금 수정했습니다.
— 크리스 다운

3

널 문자 '\ 0'과 개행 문자 '\ n'만 포함 된 파일을 찾으십시오. 에 나오지 원인 각 파일은 행에 null 이외의 문자를 발견 즉시 종료 검색 할 수 있습니다.
q

find -type f -name 'file-*' |
  while IFS= read -r file ;do 
      out=$(sed -n '1=; /^\x00\+$/d; i non-null
                      ; q' "$file")
      [[ $out == "1" ]] &&  echo "$file"
  done

테스트 파일 만들기

> file-empty
printf '%s\n' 'line1' 'line2' 'line3'      > file-with-text           
printf '%4s\n' '' '' xx | sed 's/ /\x00/g' > file-with-text-and-nulls
printf '%4s\n' '' '' '' | sed 's/ /\x00/g' > file-with-nulls-and-newlines
printf '%4s'   '' '' '' | sed 's/ /\x00/g' > file-with-nulls-only

산출

./file-with-nulls-and-newlines
./file-with-nulls-only

중 하나를 -print0인수에서 누락 된 것 같다 find또는 IFS=부분은 엉망이된다. 의도 된 구분자는 무엇입니까?
— 타이슨

3

이 one-liner는 GNU를 사용하여 100 % NUL 파일을 찾는 가장 효율적인 방법 find이며 xargs, grep후자는 PCRE 지원으로 빌드되었다고 가정합니다.

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00]" --

제공된 다른 답변에 비해이 방법의 장점은 다음과 같습니다.

  • 스파 스가 아닌 파일이 검색에 포함됩니다.
  • 읽을 수없는 파일은 grep으로 전달되지 않으므로 Permission denied경고 가 발생하지 않습니다 .
  • grep널이 아닌 바이트를 찾은 후 파일에서 데이터 읽기를 중지합니다 ( LC_ALL=C각 바이트 가 문자 로 해석되도록하는 데 사용됨) ).
  • 빈 파일 (0 바이트)은 결과에 포함되지 않습니다.
  • 더 적은 grep프로세스가 여러 파일을 효율적으로 확인합니다.
  • 개행 문자를 포함하거나로 시작하는 경로 -는 올바르게 처리됩니다.
  • Python / Perl이없는 대부분의 임베디드 시스템에서 작동합니다.

-Z옵션을 전달 grep하고 사용 xargs -r0 ...하면 100 % 널 파일에 대해 추가 조치를 수행 할 수 있습니다 (예 : 정리).

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00]" -- |
  xargs -r0 rm --

또한 심볼릭 링크를 따르지 않고 파일 시스템 통과 (예 : 원격 마운트, 장치 트리, 바인드 마운트 등) 를 피하기 위해 find옵션 -P을 사용하는 것이 좋습니다 -xdev.

줄 끝 문자 를 무시하기 위해 다음 변형이 작동해야합니다 (그러나 이것이 좋은 생각은 아닙니다) :

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00\r\n]" --

원하지 않는 파일 (100 % 널 / 줄 바꾸기 문자)을 제거하여 파일이 백업되지 않도록하는 것을 포함하여이를 모두 정리하십시오.

find -P . -xdev -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00\r\n]" -- |
  xargs -0 rm --

빈 파일 (0 바이트)을 포함하지 않는 것이 좋습니다. 종종 매우 구체적인 목적으로 존재 합니다 .


많은 대안 중에서 가장 빠른 것이 대담한 주장입니다. 벤치 마크 :-)를 추가하면 답변이 수락 된 것으로 표시됩니다.
— Adam Ryczkowski

이러한 벤치 마크는 다양한 디스크 서브 시스템의 성능을 포함하여 많은 요소에 따라 달라집니다.
— 타이슨

물론, 아무것도 아닌 것보다 낫습니다. 다양한 접근 방식이 CPU 사용을 다르게 최적화하므로 SSD 또는 캐시 된 파일에서 CPU를 벤치마킹하는 것이 좋습니다. 현재 작업중인 컴퓨터를 가져 와서 한 문장 (CPU 유형, 코어 없음, RAM, 하드 드라이브 유형)을 작성하고 파일 세트를 설명하십시오 (예 : 커널 소스 클론 + \0900MB 홀로 가득 찬 1GB 파일 ). 결과의 현재 타이밍. 벤치 마크가 당신을 위해 설득력있는 방식으로 그렇게한다면, 아마 우리 모두에게 유죄 판결을받을 것입니다
— Adam Ryczkowski

"대부분의 임베디드 시스템"에는 GNU 유틸리티가 없습니다. 비지 박스 일 가능성이 높습니다.
— Stéphane Chazelas

-P의 기본값입니다 find. 심볼릭 링크를 따르려면 -L/ -follow입니다. POSIX는 해당 옵션을 지정하지도 않습니다 find(POSIX는 몇 가지 명령에 대해 -P / -H / -L을 도입 한 것이더라도).
— Stéphane Chazelas

0

GNU sed를 사용하기 위해 -z옵션을 사용할 수 있습니다.이 옵션은 줄을 0으로 끝나는 문자열로 정의하고 빈 줄을 일치시키고 삭제합니다.

if [ "$( sed -z '/^$/d' "$file" | head -c 1 | wc -c )" -eq 0 ]; then
    echo "$file contains only NULL!"
fi

그 사이의 헤드 명령은 최적화 일뿐입니다.


-1

파이썬

단일 파일

별명을 정의하십시오.

alias is_binary="python -c 'import sys; sys.exit(not b\"\x00\" in open(sys.argv[1], \"rb\").read())'"

그것을 테스트하십시오 :

$ is_binary /etc/hosts; echo $?
1
$ is_binary `which which`; echo $?
0

여러 파일

모든 이진 파일을 재귀 적으로 찾기 :

IS_BINARY='import sys; sys.exit(not b"\x00" in open(sys.argv[1], "rb").read())'
find . -type f -exec bash -c "python -c '$IS_BINARY' {} && echo {}" \;

모든 비 바이너리 파일, 변경 찾으려면 &&와를 ||.


1
이 질문은 줄 바꿈을 무시하고 널 문자 만 포함하는 파일을 식별하도록 요청했으며 여기에 제공된 Python 코드는 널 문자를 포함 하는 파일을 식별합니다 .
— 타이슨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.