是否可以使用该find
命令查找目录中的所有“非二进制”文件?这就是我要解决的问题。
我收到了一位 Windows 用户发来的文件存档。该存档包含源代码和图像文件。我们的构建系统不能很好地处理具有 Windows 行结尾的文件。我有一个命令行程序 ( flip -u
),它将翻转 *nix 和 windows 之间的行结尾。所以,我想做这样的事情
find . -type f | xargs flip -u
但是,如果针对图像文件或其他二进制媒体文件运行此命令,则会损坏该文件。我意识到我可以构建一个文件扩展名列表并用它进行过滤,但我宁愿拥有一些不依赖于我保持该列表最新的东西。
那么,有没有办法找到目录树中的所有非二进制文件呢?或者我应该考虑其他解决方案?
答案1
我将使用file
并将输出通过管道传输到 grep 或 awk 中来查找文本文件,然后仅提取file
输出的文件名部分并将其通过管道传输到 xargs 中。
就像是:
file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u
请注意,grep 搜索“ASCII 文本”而不是任何“文本” - 您可能不想弄乱富文本文档或 unicode 文本文件等。
您还可以使用find
(或其他方式)生成要检查的文件列表file
:
find /path/to/files -type f -exec file {} + | \
awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u
xargs 的参数-d'\n'
使 xargs 将每个输入行视为一个单独的参数,从而满足带有空格和其他有问题字符的文件名的需要。即,它是xargs -0
输入源不或不能生成 NULL 分隔的输出(例如find
's-print0
选项)时的替代方案。根据变更日志,xargs 在 2005 年 9 月获得了-d
/--delimiter
选项,因此应该出现在任何非古老的 Linux 发行版中(我不确定,这就是我检查的原因 - 我只是隐约记得这是“最近”添加的)。
请注意,换行符是文件名中的有效字符,因此如果任何文件名中包含换行符,则会中断。对于典型的 UNIX 用户来说,这在病态上是疯狂的,但如果文件源自 Mac 或 Windows 计算机,则并非闻所未闻。
另请注意,这file
并不完美。它非常擅长检测文件中的数据类型,但有时会感到困惑。
过去我曾多次使用此方法的多种变体,并取得了成功。
答案2
find . -type f -exec grep -I -q . {} \; -print
这将找到-type f
当前目录(或下面)中所有grep
认为非空且非二进制的常规文件 ( )。
它用于grep -I
区分二进制文件和非二进制文件。当检测到文件是二进制文件时,该-I
标志将导致grep
以非零退出状态退出。根据grep
,“二进制”文件是包含可打印 ASCII 范围之外的字符的文件。
如果找到给定的模式,则该-q
选项将导致其以零退出状态退出,而不发出任何数据。grep
我们使用的模式是一个点,它将匹配任何字符。
如果发现该文件是非二进制文件并且它至少包含一个字符,则打印该文件的名称。
如果你足够勇敢,你flip -u
也可以将你的代码插入其中:
find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
答案3
接受的答案并没有为我找到所有这些。这是一个使用 grep-I
忽略二进制文件并忽略所有隐藏文件的示例......
find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo
这是在实际应用中的使用:dos2unix
答案4
bash
仅使用以下方式处理非二进制文件的通用解决方案file -b --mime-encoding
:
while IFS= read -d '' -r file; do
[[ "$(file -b --mime-encoding "$file")" = binary ]] &&
{ echo "Skipping $file."; continue; }
echo "Processing $file."
# ...
done < <(find . -type f -print0)
我联系了作者文件实用程序,他-00
在版本 5.26(2016-04-16 发布,例如在当前的 Arch 和 Ubuntu 16.10 中)添加了一个漂亮的参数,它可以file\0result\0
一次打印多个文件,这样您可以执行以下操作:
find . -type f -exec file -00 --mime-encoding {} + |
awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | …
(这awk
部分是过滤掉每个非二进制文件。ORS
是输出分隔符。)
当然也可以在循环中使用:
while IFS= read -d '' -r file; do
echo "Processing $file."
# ...
done < <(find . -type f -exec file -00 --mime-encoding {} + |
awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')
基于此和上一个,我创建了一个bash
用于过滤二进制文件的小脚本,该脚本在较新版本中使用新方法,并使用参数-00
,file
并在旧版本上回退到以前的方法:
#!/bin/bash
# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
# filter_binary_files.sh [FILES...]
#
# EXAMPLE:
# find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
#
[[ $# -eq 0 ]] && exit
if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
file -00 --mime-encoding -- "$@" |
awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
for f do
[[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
printf '%s\0' "$f"
done
fi
或者这里是一个更 POSIX-y 的,但它需要支持sort -V
:
#!/bin/sh
# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
# filter_binary_files.sh [FILES...]
#
# EXAMPLE:
# find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
#
[ $# -eq 0 ] && exit
if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
'file-5.26' ]; then
file -00 --mime-encoding -- "$@" |
awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
for f do
[ "$(file -b --mime-encoding -- "$f")" != binary ] &&
printf '%s\0' "$f"
done
fi