如何判断一个文件是二进制文件?
例如,编译后的 C 文件。
我想从某个目录读取所有文件,但我想忽略二进制文件。
如何判断一个文件是二进制文件?
例如,编译后的 C 文件。
我想从某个目录读取所有文件,但我想忽略二进制文件。
使用实用工具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
参数? :) - Bachfile -bL --mime "$path" | grep -q '^text'
。选项 -b
从输出中移除了文件名,并且 -L
解引用符号链接。 - wjandrea--mime
标志,否则匹配所有可能的二进制格式的 file
输出不现实(这样的正则表达式会太长且易碎)。 - yugrfile -bL --mime "/my/file/path/some_binary" | grep -q "charset=binary" && echo "binary file"
。 - undefined改编自排除二进制文件
find . -exec file {} \; | grep text | cut -d: -f1
file
的 manpage,应该是 text
。 - gongzhitaaofind . -type f -exec file {} \; | grep -v text | cut -d: -f1
。 - Gerritfile -b
,它不会输出文件名。(可能是GNU特有的功能)。 - wjandrea我使用
! grep -qI . "$path"
唯一的缺点是它会将空文件视为二进制文件,但又有谁能决定这是否错误?
基于@mgutt的建议进行编辑:
在某些情况下,文件可能非常大,因此根据您需要做什么,只读取文件的一部分可能更安全且足够。
head -c 1024 "$path" | grep -qI .
|| ! test -s $path
来控制空文件的情况。 - yugr''
),而非任何单个字符('.'
):**! fgrep -qI '' "$path"
**。通过这种方式,空文件和仅由换行标记(换行符)组成的文件将被视为文本文件。 - Sashahead -c 1024 "$path" | grep -qIF ''
命令?! - mguttgrep
这是一个使用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字符,因此可以检查其他非ASCII字符,例如:
$ grep -P "[^\x00-\x7F]" file && echo Binary || echo Text
grep
?使用 GNU grep 3.1,搜索 \x00
总是失败。 - Vladimir Panteleevperl -E 'exit((-B $ARGV[0])?0:1);' file-to-test
可以用来检查“file-to-test”是否为二进制文件。以上命令将在二进制文件上以退出代码0退出,否则退出代码将为1。
检查文本文件的反向检查可能类似于以下命令:
perl -E 'exit((-T $ARGV[0])?0:1);' file-to-test
perldoc -f -X
了解有关-B
和-T
检查的更多信息。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
用于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 |^@^@|
其中^@
字符代表空字符。因此,一旦发现这些控制字符,我们就认为文件是二进制的。
上述方法的缺点是,在字符不表示控制字符时可能会产生误报。例如:
$ printf "\x00\x00^@^@" | cat -v | hexdump -C
00000000 5e 40 5e 40 5e 40 5e 40 |^@^@^@^@|
\^@
时。 - Vladimir Panteleev使用 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
根据Bach的建议,我认为--mime-encoding
是从file
获取可靠结果的最佳标志。
file --mime-encoding [FILES ...] | grep -v '\bbinary$'
该命令将打印文件,这些文件被file
认为具有非二进制编码。如果您只想要文件名,则可以通过cut -d: -f1
将此输出传输。
注意:正如@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
). - yugrfile
中的一个错误,因为 .docx
文件在 --mime-encoding
下打印出了 binary
。 - dimo414file "$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
' _ '{}' +