내용에서 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.