【发布时间】:2013-05-21 13:18:56
【问题描述】:
如何知道文件是否为二进制文件?
例如编译的c文件。
我想从某个目录读取所有文件,但我想忽略二进制文件。
【问题讨论】:
-
最终所有文件都是二进制文件。文本文件恰好包含人类可读字符数据的二进制表示。没有任何一种区分文本和非文本的方法是 100% 可靠的。
标签: shell unix binaryfiles
如何知道文件是否为二进制文件?
例如编译的c文件。
我想从某个目录读取所有文件,但我想忽略二进制文件。
【问题讨论】:
标签: shell unix binaryfiles
也许这就够了..
if ! file /path/to/file | grep -iq ASCII ; then
echo "Binary"
fi
if file /path/to/file | grep -iq ASCII ; then
echo "Text file"
fi
【讨论】:
离开Bach's suggestion,我认为--mime-encoding 是从file 获得可靠信息的最佳标志。
file --mime-encoding [FILES ...] | grep -v '\bbinary$'
将打印file 认为具有非二进制编码的文件。如果您只想要文件名,您可以通过cut -d: -f1 管道输出以修剪: encoding。
警告:@yugr 在.doc 文件下方报告的编码为application/mswordbinary。这在我看来像一个错误 - mime 类型错误地与编码连接。
$ for flag in --mime --mime-type --mime-encoding; do
echo "$flag"
file "$flag" /tmp/example.{doc{,x},png,txt}
done
--mime
/tmp/example.doc: application/msword; charset=binary
/tmp/example.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary
/tmp/example.png: image/png; charset=binary
/tmp/example.txt: text/plain; charset=us-ascii
--mime-type
/tmp/example.doc: application/msword
/tmp/example.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document
/tmp/example.png: image/png
/tmp/example.txt: text/plain
--mime-encoding
/tmp/example.doc: application/mswordbinary
/tmp/example.docx: binary
/tmp/example.png: binary
/tmp/example.txt: us-ascii
【讨论】:
--mime 确实有效 (application/msword; charset=binary)。
file 中的一个错误,因为 .docx 文件为 --mime-encoding 打印 binary。
.doc bug was fixed。
grep假设二进制意味着文件包含不可打印字符(不包括空格、制表符或换行符等空白字符),这可能有效(BSD 和 GNU):
$ grep '[^[:print:][:blank:]]' file && echo Binary || echo Text
注意:GNU grep 会将仅包含 NULL 字符的文件报告为文本,但它会在 BSD version 上正常工作。
【讨论】:
cat+grep
假设二进制是指包含 NULL 字符的文件,这个 shell 命令可以提供帮助:
(cat -v file.bin | grep -q "\^@") && echo Binary || echo Text
或:
grep -q "\^@" <(cat -v file.bin) && echo Binary
这是grep -q "\x00" 的解决方法,它适用于 BSD grep,但不适用于 GNU 版本。
基本上-v for cat 转换所有非打印字符,以便它们以控制字符的形式可见,例如:
$ printf "\x00\x00" | hexdump -C
00000000 00 00 |..|
$ printf "\x00\x00" | cat -v
^@^@
$ printf "\x00\x00" | cat -v | hexdump -C
00000000 5e 40 5e 40 |^@^@|
其中^@ 字符表示 NULL 字符。所以一旦找到这些控制字符,我们就假设文件是二进制文件。
上述方法的缺点是当字符不代表控制字符时会产生误报。例如:
$ printf "\x00\x00^@^@" | cat -v | hexdump -C
00000000 5e 40 5e 40 5e 40 5e 40 |^@^@^@^@|
【讨论】:
\^@ 的文本文件。
grep这是使用BSD grep(在 macOS/Unix 上)检查单个文件的简单解决方案:
grep -q "\x00" file && echo Binary || echo Text
它基本上检查文件是否包含 NUL 字符。
使用此方法,您可以使用find 实用程序递归读取所有非二进制文件:
find . -type f -exec sh -c 'grep -q "\x00" {} || cat {}' ";"
或者更简单地使用grep:
grep -rv "\x00" .
对于当前文件夹,使用:
grep -v "\x00" *
很遗憾,上述示例不适用于GNU grep,但有一种解决方法。
grep由于 GNU grep 会忽略 NULL 字符,因此 check for other non-ASCII characters 可能会:
$ grep -P "[^\x00-\x7F]" file && echo Binary || echo Text
注意:它不适用于仅包含 NULL 字符的文件。
【讨论】:
grep 是哪个版本的?使用 GNU grep 3.1,搜索 \x00 总是失败。
grep 版本,请检查。
【讨论】:
我用
! grep -qI . $path
我能看到的唯一缺点是它会考虑一个空文件二进制文件,但话又说回来,谁来决定这是否是错误的?
【讨论】:
|| ! test -s $path来控制。
''),而不是任何单个字符 ('.'):! fgrep -qI '' "$path"。这样,空文件和仅包含换行符(换行符)的文件将被视为文本。
试试下面的命令行:
file "$FILE" | grep -vq 'ASCII' && echo "$FILE is binary"
【讨论】:
使用tr -d "[[:print:]\n\t]" < file | wc -c 排除二进制文件是一种蛮力,但也不是启发式猜测。
find . -type f -maxdepth 1 -exec /bin/sh -c '
for file in "$@"; do
if [ $(LC_ALL=C LANG=C tr -d "[[:print:]\n\t]" < "$file" | wc -c) -gt 0 ]; then
echo "${file} is no ASCII text file (UNIX)"
else
echo "${file} is ASCII text file (UNIX)"
fi
done
' _ '{}' +
不过,以下使用 grep -a -m 1 $'[^[:print:]\t]' file 的蛮力方法似乎要快一些。
find . -type f -maxdepth 1 -exec /bin/sh -c '
tab="$(printf "\t")"
for file in "$@"; do
if LC_ALL=C LANG=C grep -a -m 1 "[^[:print:]${tab}]" "$file" 1>/dev/null 2>&1; then
echo "${file} is no ASCII text file (UNIX)"
else
echo "${file} is ASCII text file (UNIX)"
fi
done
' _ '{}' +
【讨论】:
perl -E 'exit((-B $ARGV[0])?0:1);' file-to-test
可用于检查“待测试文件”是否为二进制文件。上面的命令将退出二进制文件的机智代码 0,否则退出代码将为 1。
对文本文件的反向检查可能类似于以下命令:
perl -E 'exit((-T $ARGV[0])?0:1);' file-to-test
同样,如果“待测试文件”是文本(不是二进制),上述命令将以状态 0 退出。
使用命令perldoc -f -X 阅读有关-B 和-T 检查的更多信息。
【讨论】:
使用 Perl 的内置 -T 文件测试运算符,最好在使用 -f 文件测试运算符确定它是纯文件之后:
$ perl -le 'for (@ARGV) { print if -f && -T }' \
getwinsz.c a.out /etc/termcap /bin /bin/cat \
/dev/tty /usr/share/zoneinfo/UTC /etc/motd
getwinsz.c
/etc/termcap
/etc/motd
这是该集合的补集:
$ perl -le 'for (@ARGV) { print unless -f && -T }' \
getwinsz.c a.out /etc/termcap /bin /bin/cat \
/dev/tty /usr/share/zoneinfo/UTC /etc/motd
a.out
/bin
/bin/cat
/dev/tty
/usr/share/zoneinfo/UTC
【讨论】:
find . -exec file {} \; | grep text | cut -d: -f1
【讨论】:
grep text;从历史上看,file 并不总是说 ASCII,而是例如“shell 脚本文本”。
file 手册页,它应该是text。
find . -type f -exec file {} \; | grep -v text | cut -d: -f1
file -b,它不输出文件名。 (可能是 GNU 独有的功能)。
使用实用程序file,示例用法:
$ file /bin/bash
/bin/bash: Mach-O universal binary with 2 architectures
/bin/bash (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/bash (for architecture i386): Mach-O executable i386
$ file /etc/passwd
/etc/passwd: ASCII English text
$ file code.c
code.c: ASCII c program text
【讨论】:
--mime? :)
file -bL --mime "$path" | grep -q '^text'。选项 -b 从输出中删除文件名,-L 取消引用符号链接。
--mime 标志,否则对于所有可能的二进制格式匹配 file 的输出是不现实的(这样的正则表达式会太长且脆弱)。