파일을 "이진"또는 "텍스트"로 분류하는 편리한 방법이 있습니까?


35

같은 표준 유닉스 유틸리티 grepdiff"텍스트"또는 "진"으로 분류 파일에 약간의 휴리스틱을 사용합니다. 예를 들어, grep출력에와 같은 줄이 포함될 수 있습니다 Binary file frobozz matches.

zsh비슷한 "텍스트 / 이진"분류를 수행하기 위해 스크립트에 적용 할 수있는 편리한 테스트가 있습니까? (와 같은 것 이외 grep '' somefile | grep -q Binary)

(나는 그러한 테스트가 반드시 휴리스틱이므로 불완전하다는 것을 알고 있습니다.)


10
file표준 유틸리티이며 파일 형식을 결정하기 위해 파일 마법을 실행할 수 있습니다. 대부분의 텍스트 형식을 말할 수 있으며 이진 형식에서 꽤 괜찮은 일을합니다. 당신이하려는 모든 것이 파일이 텍스트인지 아닌지를 알아내는 것이라면, 이것이 당신이 관심있는 명령입니다.
Bratchley

@Bratchley : "텍스트"로 분류하려는 일부 파일 의 file경우 일부 버전 이 인쇄됩니다 shell script. 얻을 수있는 방법이 있나요 file바로 인쇄 text하거나 binary?
kjo

1
@don_crissti이 질문은 사람들이 자신의 bash 스크립트를 디버깅하도록하는 사람에 관한 것입니다. 텍스트를 감지하는 것은 스크립트가해야 할 일입니다. 그들은 cut명령 중 하나에 문제가 생겼습니다 .
Bratchley

1
@don_crissti 질문 B에 대해 작동하는 질문 A에 대한 답이 항상 A를 B의 사본으로 만들지는 않습니다. 파일을 텍스트 또는 이진으로 분류하는 방법을 찾고있는 사람을 고려하십시오. 다음 중 어느 것이 더 유용합니까? "스크립트 디버깅"질문은 해당 스크립트와 관련된 다른 답변들 사이에 일반 답변이 묻혀 있거나 일반적인 "필자는 텍스트 또는 이진으로 분류하는 방법"입니까?
Gilles 'SO- 악의를 멈춰라'

1
@Gilles-읽는 방법에 따라 다릅니다. 필자는 실제로 XY 문제의 전형적인 사례로 질문을 봅니다. OP는 파일이 텍스트 파일 인지 확인 하고 파이프 file출력을 cut솔루션 이라고 생각합니다. 확실히 누락 된 공간이있어서 실패했습니다. 대부분의 사람들은 X 대신 Y를 언급하지만 Stéphane의 의견과 답변은 파일이 텍스트인지 아닌지를 결정하는 올바른 방법을 보여줍니다.
don_crissti

답변:


27

당신이 묻는다면 file단지에 대한 MIME 형식 이 같은 많은 다른 것들을 얻을 것이다 text/x-shellscript, 및 application/x-executable등,하지만 당신은 당신이 좋은 결과를 얻을해야 "텍스트"부분을 확인하면 나는 상상한다. 예 ( -b출력에 파일 이름이없는 경우) :

file -b --mime-type filename | sed 's|/.*||'

24
그냥에 따라 기억 file이 텍스트 형식을 놓칠 수도 : application/xml(및 RSS와 같은 유사), application/ecmascript, application/json, image/svg+xml, ... 당신은 화이트리스트에 사람들이있을 것이다.
Boldewyn

@Boldewyn 와우, 좋은 예! 따라서 더 나은 대답은 인쇄 가능한 문자 만있는 파일을 수락하는 것이지만 utf-8 및 유사한 인코딩 문제에도 대처할 수 있습니다.
meuh

예, 아래 답변의 요지입니다. 문제는 그 솔루션이 전체 파일 을 살펴 봐야한다는 것입니다 .
Boldewyn

7
@Boldewyn 원칙적으로 application/*타입은 개발과 디버깅을 용이하게하기 위해 텍스트 기반 일지라도 사람이 소비하기위한 것이 아닙니다. 그래서 a text/xml와 a가 모두 application/xml있습니다. 따라서 텍스트로 간주할지 여부는 OP의 요구에 달려 있습니다.
Tobia

3
또는cut -d/ -f1
Stéphane Chazelas

20

또 다른 방법은 moreutils 컬렉션 isutf8에서 사용 하는 입니다.

파일이 유효한 UTF-8 또는 ASCII 인 경우 0으로 종료되거나 단락이 발생하고 오류 메시지 (로 -q표시됨)를 인쇄하고 그렇지 않으면 1로 종료됩니다.


5
좋은 제안입니다. 방금 arg로 디렉토리를 제공하면 0을 반환한다는 것을 알았습니다. 적어도 1을 선호했을 것입니다. 그러나 쓰레기는 쓰레기가됩니다.
meuh

13

GNU grep에서 사용하는 휴리스틱을 좋아한다면 다음과 같이 사용할 수 있습니다.

isbinary() {
  LC_MESSAGES=C grep -Hm1 '^' < "${1-$REPLY}" | grep -q '^Binary'
}

그것은 NUL이 첫 번째 버퍼가 파일에서 읽은 바이트를 검색 (일반 파일에 대한 몇 킬로 바이트,하지만 훨씬 덜 파이프 나 소켓 또는 일부 장치 등에 수 /dev/random). UTF-8 로켈에서는 유효한 UTF-8 문자를 형성하지 않는 바이트 시퀀스에 플래그를 지정합니다. LC_ALL언어가 영어가 아닌 것으로 설정되어 있지 않은 것으로 가정합니다 .

${1-$REPLY}양식을 통해 zshglob 한정자 로 사용할 수 있습니다 .

ls -ld -- *(.+isbinary)

이진 파일을 나열 합니다.


7

iconv파일을 읽을 수 있는지 판별 할 수 있습니다. 이것은 file처음부터 몇 바이트를 읽는 것보다 성능이 떨어지지 만보다 안정적인 결과를 제공합니다.

ENCODING=utf-8
if iconv --from-code="$ENCODING" --to-code="$ENCODING" your_file.ext > /dev/null 2>&1; then
    echo text
else
    echo binary
fi

이것은 iconv기본적으로 no-op이지만, 유효하지 않은 데이터 (이 예에서는 유효하지 않은 UTF-8)가 발생하면 barf 및 exit됩니다.


4
GNU long 옵션 대신 -f-t을 사용하면 이식성이 향상됩니다. 열 수없는 파일을 "이진"이라고합니다. 빈 파일을 "텍스트"라고 부릅니다.
Stéphane Chazelas

동의했다. 나는 모르는 사람들을 위해 임시 문서에 긴 양식을 사용했습니다 iconv. 하지만 -f과는 -t일반적으로 더 낫다.
Boldewyn

7

을 호출하는 스크립트를 작성하고 filecase-statement를 사용하여 관심있는 케이스를 확인할 수 있습니다.

예를 들어

#!/bin/sh
case $(file "$1") in
(*script*|*\ text|*\ text\ *)
    echo text
    ;;
(*)
    echo binary
    ;;
esac

물론 관심있는 특별한 경우가 많이있을 수 있습니다. strings의 사본을 확인 하면 libmagic약 200 건이 표시됩니다 (예 :

Konqueror cookie text
Korn shell script text executable
LaTeX 2e document text
LaTeX document text
Linux Software Map entry text
Linux Software Map entry text (new format)
Linux kernel symbol map text
Lisp/Scheme program text
Lua script text executable
LyX document text
M3U playlist text
M4 macro processor script text

일부는 문자열 "text"를 다른 유형의 일부로 사용합니다. 예 :

SoftQuad troff Context intermediate   
SoftQuad troff Context intermediate for AT&T 495 laser printer
SoftQuad troff Context intermediate for HP LaserJet

마찬가지로 script단어의 일부가 될 수 있지만이 경우 아무런 문제가 없습니다. 그러나 스크립트는 하위 문자열이 아닌 단어"text" 로 확인해야합니다 .

다시 말해, file출력은 항상 "스크립트"또는 "텍스트"를 갖는 정확한 설명을 사용하지 않습니다. 특별한 경우를 고려해야합니다. 후속 조치는 파일 --mime-type에 대해서는이 방법은 효과가 없지만 작동 한다고 언급했습니다 .svg. 그러나 테스트에서 svg-files에 대한 다음 결과를 볼 수 있습니다.

$ ls -l *.svg
-r--r--r-- 1 tom users  6679 Jul 26  2012 pumpkin_48x48.svg
-r--r--r-- 1 tom users 17372 Jul 30  2012 sink_48x48.svg
-r--r--r-- 1 tom users  5929 Jul 25  2012 vile_48x48.svg
-r--r--r-- 1 tom users  3553 Jul 28  2012 vile-mini.svg
$ file *.svg
pumpkin_48x48.svg: SVG Scalable Vector Graphics image
sink_48x48.svg:    SVG Scalable Vector Graphics image
vile-mini.svg:     SVG Scalable Vector Graphics image
vile_48x48.svg:    SVG Scalable Vector Graphics image
$ file --mime-type *.svg
pumpkin_48x48.svg: image/svg+xml
sink_48x48.svg:    image/svg+xml
vile-mini.svg:     image/svg+xml
vile_48x48.svg:    image/svg+xml

수천 개의 파일을 본 후 선택한 MIME 형식 출력에서 ​​"텍스트"와 함께 6 만 표시합니다. 틀림없이, 마임 타입 출력의 끝 부분에있는 "XML을"일치하는 것은 일치 "SVG"보다 말하지만, 스크립트를 사용하여, 더 유용 할 수 제안이 여기에서에 그 돌아갑니다.

출력은 file어느 시나리오에서나 약간의 조정 이 필요하며 100 % 신뢰할 수 없습니다 ( "펄"스크립트 중 일부는 "데이터"라고 혼동합니다).

의 구현이 둘 이상 있습니다 file. 가장 일반적으로 사용되는 프로그램은에서 작동하며 libmagic다른 프로그램에서 사용할 수 있습니다 (아마도 직접 zsh하지는 않지만 python가능).

쉘, Perl, Ruby 및 Python의 파일 테스트 비교 테이블에 따르면 Perl에는 -T이 정보를 제공하는 데 사용할 수 있는 옵션이 있습니다. 그러나 이에 대한 기능은 없습니다 zsh.

더 읽을 거리 :


불행히도 filesvg 파일에 대한 GNU 출력 : SVG Scalable Vector Graphics image단어 텍스트를 포함하지 않습니다. 나는이 접근법이 MIME 유형을 확인하는 데 허용되는 대답보다 낫다고 생각했지만 여전히 일부 유형이 누락되었습니다.
Peter Cordes

MIME 유형으로 여전히 그리워합니다. xterm의 svg 파일의 경우을 얻습니다 image/svg+xml. 실제로-1000 파일을 동일하게 확인했는데 mime 유형에 따라 6 개만 "텍스트"로 나왔습니다. 최소한 필요에 따라 작동하도록 스크립트를 작성하겠습니다.
Thomas Dickey

3

file--mime-encoding파일의 인코딩을 감지 하는 옵션 이 있습니다.

 $file --mime-encoding Documents/poster2.pdf 
Documents/poster2.pdf: binary
 $file --mime-encoding projects/linux/history-torvalds/Makefile 
projects/linux/history-torvalds/Makefile: us-ascii
 $file --mime-encoding graphe.tex 
Dgraphe.tex: us-ascii
 $file --mime-encoding software.tex 
software.tex: utf-8

file --mime-encoding | grep binary파일이 이진 파일인지 감지하는 데 사용할 수 있습니다 . 긴 텍스트 파일에서 하나의 잘못된 문자로 혼동 될 수 있지만 안정적으로 작동합니다.

예를 들어, cat이진 파일을 실수로 열어 터미널을 망치지 않도록 다음 셸 스크립트의 별칭 을 지정합니다.

#! /bin/sh -

[ ! -t 1 ] && exec /bin/cat "$@"
for i
do
    if file --mime-encoding -- "$i" | grep -q binary
    then
        hexdump -C -- "$i"
    else
        /bin/cat -- "$i"
    fi
done

3

카테고리는 임의적입니다. 분류하는 방법에 대답하기 전에 (엄격한) 정의가 필요합니다. 정의를 가지 려면 목적이 필요합니다 .

그렇다면 그 분류로 무엇을하고 싶습니까?

  • FTP에서 ascii / binary를 선택하려면 바이너리 파일을 ascii로 전송하지 마십시오 (또는 손상 될 수 있음). 따라서 파일이 일반 텍스트, html, rtf 및 기타 파일인지 테스트해야합니다. 그러나 의심스러운 것은 바이너리를 선택하십시오. 또한 파일에 0x0A, 0x0D 및 0x20-0x7F와 같은 하위 집합 만 있는지 테스트하고 싶을 수도 있습니다.
  • 일부 프로토콜 (POP3, SMTP)로 파일을 전송하려면 base64로 인코딩 할 것인지 일반으로 인코딩 할 것인지 테스트해야합니다. 이 경우 지원되지 않는 문자가 있는지 테스트해야합니다.
  • 다른 경우에는… 다른 정의가있을 수 있습니다.

3
perl -e'chomp(my$f=<>);print "binary$/" if -B $f;print "text$/" if -T _'

할 것입니다. 및에 대한 설명서를-B-T 참조하십시오 (문자열에 대한 해당 페이지 검색 The -T and -B switches work as follows).


perl -le 'print -B $ARGV[0] ? "binary" : "text"' --더 명확 할 수 있습니다. 심지어perl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982 모니카 지원


1

나는 지금이 대답은 조금 낡았지만, 내 친구가 나에게 이것을하기 위해 큰 "해킹"을 가르쳤다 고 생각한다.

diff명령을 사용하고 테스트 텍스트 파일과 비교하여 파일을 확인하십시오.

$ diff filetocheck testfile.txt

이제 filetocheck이진 파일이면 출력은 다음과 같습니다.

Binary files filetocheck and testfile.txt differ

이런 식으로 diff명령을 활용하고 스크립트에서 검사를 수행하는 함수를 작성할 수 있습니다.

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