是否有方便的方法将文件分类为“二进制”或“文本”?


35

标准Unix实用程序喜欢grepdiff使用一些启发式方法将文件分类为“文本”或“二进制”。(例如grep,输出中可能包含类似的行Binary file frobozz matches。)

是否有一种方便的测试方法可以应用到zsh脚本中以执行类似的“文本/二进制”分类?(除了类似的东西grep '' somefile | grep -q Binary。)

(我意识到任何此类测试都必然是启发式的,因此是不完善的。)


10
file是一个标准实用程序,可以运行文件魔术来尽其所能确定文件类型。它可以分辨大多数文本格式,并且在二进制格式上做得相当不错。如果您要做的只是找出文件是否为文本,这就是您感兴趣的命令
。– Bratchley

@Bratchley:file将打印某些版本的,例如shell script对于某些我想归类为“文本”的文件。有没有办法file打印正本textbinary
kjo

1
@don_crissti这个问题是关于有人试图让人们调试他的bash脚本。检测文本正是脚本应该执行的操作。他们最终在其中一个cut命令中遇到了问题。
Bratchley '16

1
@don_crissti问题A上有一个答案适用于问题B的事实并不总是使A与B重复。请考虑某人正在寻找一种将文件分类为文本或二进制的方法。哪个更有用:一个“调试我的脚本”问题,恰好在该脚本特有的其他答案中掩盖了一个通用答案,或者一个通用的“如何将字段分类为文本或二进制?”?
吉尔斯(Gilles)“所以,别再邪恶了”

1
@Gilles-取决于您的阅读方式。我实际上将那里的问题看作是XY问题的典型案例:OP那里要检查文件是否是文本文件-并认为将管道file输出到cut解决方案-可以肯定的是,缺少空间会使其失败,并且那里的大多数人用Y代替X,但是Stéphane的注释和答案显示了确定文件是否为文本的正确方法。
don_crissti

Answers:


27

如果file只要求mime类型,您会得到许多不同的字符,例如text/x-shellscriptapplication/x-executable等,但是我想如果只检查“文本”部分,您应该会得到不错的结果。例如(-b输出中没有文件名):

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

24
请记住,这取决于你的file,你可能会错过一些文本格式:application/xml(以及类似像RSS) ,,application/ecmascript ,...你得白名单的。application/jsonimage/svg+xml
Boldewyn

@Boldewyn哇,很好的例子!因此,可能更好的答案是只接受仅具有可打印字符的任何文件,但以某种方式还可以处理utf-8和类似的编码问题。
meuh's

是的,这就是我在下面回答的要点。唯一的问题是,该解决方案必须查看整个文件...
Boldewyn

7
@Boldewyn原则上,application/*类型不是供人类使用的,即使它们可能是基于文本的以便于开发和调试。这就是为什么同时存在a text/xml和an的原因application/xml。因此,是否将其视为文本的问题取决于OP的需求。
Tobia'Apr

3
或者cut -d/ -f1
斯特凡Chazelas

20

另一种方法是isutf8moreutils集合中使用。

如果文件是有效的UTF-8或ASCII或短路,则以0退出,并显示一条错误消息(以表示静音-q),否则以1退出。


5
很好的建议。我只是注意到,将目录指定为arg会使它返回0。我至少希望使用1。但是然后,垃圾进来,垃圾出去。
meuh's

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}表格允许您将其用作zsh全局限定符:

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基本上是无操作的,但是如果遇到无效数据(在此示例中为无效UTF-8),它将发出声响并退出。


4
使用-f-t代替GNU的long选项将使其更具可移植性。请注意,它将调用无法打开的文件“二进制”。它将空文件称为“文本”。
斯特凡Chazelas

同意 我将长格式用于不熟悉的人的临时文档iconv。但是-f-t通常更好。
Boldewyn

7

您可以编写调用的脚本file,并使用案例陈述来检查您感兴趣的案例。

例如

#!/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文件的以下结果:

$ 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-type输出中仅显示6个带有“文本”的文件时选择了该文件。可以说,匹配mime-type输出末尾的“ xml”可能比匹配“ SVG”更有用,但是使用脚本来完成操作会使您回到此处提出的建议。

file在这两种情况下,的输出都需要进行一些调整,并且并非100%可靠(我的几个Perl脚本将其混淆,称它们为“数据”)。

的实现不止一个file。最常用的一种在中工作libmagic,可以在不同的程序中使用(也许不能直接从zsh,但python可以)。

根据Shell,Perl,Ruby和Python的文件测试比较表,Perl有一个-T选项,可以用来提供此信息。但是它没有列出与之类似的功能zsh

进一步阅读:


不幸的是GNU file的svg文件输出:SVG Scalable Vector Graphics image不包含文字。我认为这种方法会比公认的检查mime类型的答案更好,但是它仍然会遗漏某些类型。
彼得·科德斯

它仍然以mime类型错过。对于xterm的svg文件,我得到了image/svg+xml。实际上-仅检查了1000个文件,仅根据mime类型,只有6个文件作为“文本”出现。我将坚持使用脚本,至少可以根据需要使它工作。
托马斯·迪基

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为以下shell脚本添加了别名,以避免因无意中打开二进制文件而破坏我的终端:

#! /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支持Monica's


1

我现在这个答案有点老了,但是我想我的朋友教我一个很棒的“技巧”来做到这一点。

您使用diff命令并针对测试文本文件检查文件:

$ diff filetocheck testfile.txt

现在,如果filetocheck为二进制文件,则输出为:

Binary files filetocheck and testfile.txt differ

这样,您可以利用diff命令并例如编写在脚本中执行检查的功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.