查找所有“非二进制”文件

Ala*_*orm 53 find files text newlines

是否可以使用该find命令查找目录中的所有“非二进制”文件?这是我试图解决的问题。

我收到了来自 Windows 用户的文件存档。此存档包含源代码和图像文件。我们的构建系统不能很好地处理具有 Windows 行结尾的文件。我有一个命令行程序 ( flip -u) 可以在 *nix 和 windows 之间翻转行尾。所以,我想做这样的事情

find . -type f | xargs flip -u
Run Code Online (Sandbox Code Playgroud)

但是,如果针对图像文件或其他二进制媒体文件运行此命令,则会损坏该文件。我意识到我可以建立一个文件扩展名列表并用它过滤,但我宁愿有一些不依赖于我保持该列表最新的东西。

那么,有没有办法在目录树中找到所有非二进制文件?或者是否有我应该考虑的替代解决方案?

cas*_*cas 24

我会使用file并将输出通过管道传输到 grep 或 awk 中以查找文本文件,然后仅提取file's 输出的文件名部分并将其通过管道传输到 xargs 中。

就像是:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u
Run Code Online (Sandbox Code Playgroud)

请注意,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
Run Code Online (Sandbox Code Playgroud)

-d'\n'xargs的参数使 xargs 将每个输入行视为一个单独的参数,从而适应带有空格和其他有问题的字符的文件名。即它是xargs -0输入源不或不能生成空分隔输出(例如find's-print0选项)的替代方法。根据更新日志,xargs在 2005 年 9 月获得了-d/--delimiter选项,因此应该在任何非古老的 ​​linux 发行版中(我不确定,这就是我检查的原因 - 我只是依稀记得它是“最近”添加的)。

请注意,换行符是文件名中的有效字符,因此如果任何文件名中包含换行符,则会中断。对于典型的 unix 用户来说,这是病态的疯狂,但如果文件源自 Mac 或 Windows 机器,这并非闻所未闻。

另请注意,这file并不完美。它非常擅长检测文件中的数据类型,但有时会感到困惑。

过去,我曾多次成功使用这种方法的多种变体。

  • 值得一提的是 `grep -I` 过滤二进制文件 (3认同)

phy*_*att 10

接受的答案并没有为我找到所有答案。这是一个使用 grep-I忽略二进制文件并忽略所有隐藏文件的示例...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 
Run Code Online (Sandbox Code Playgroud)

这是在实际应用中使用的:dos2unix

https://unix.stackexchange.com/a/365679/112190


der*_*ert 9

没有。二进制或非二进制文件没有什么特别之处。您可以使用诸如“仅包含 0x01–0x7F 中的字符”之类的启发式方法,但这会调用具有非 ASCII 字符二进制文件的文本文件,以及不幸的二进制文件文本文件。

现在,一旦你忽略了这一点......

压缩文件

如果它来自您的 Windows 用户作为 zip 文件,则 zip 格式支持在存档本身中将文件标记为二进制或文本。您可以使用解压缩的-a选项来注意这一点并进行转换。当然,请参阅第一段,了解为什么这可能不是一个好主意(zip 程序在制作存档时可能猜错了)。

zipinfo 会在其 zipfile 列表中告诉您哪些文件是二进制 (b) 或文本 (t)。

其它文件

file 命令将查看文件并尝试识别它。特别是,您可能会发现它的-i(输出 MIME 类型)选项很有用;只转换类型为 text/* 的文件


phk*_*phk 7

仅在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)
Run Code Online (Sandbox Code Playgroud)

我联系了文件实用程序的作者,他-00在 5.26 版(2016 年 4 月 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}' | …
Run Code Online (Sandbox Code Playgroud)

(这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}')
Run Code Online (Sandbox Code Playgroud)

基于此和之前的内容,我创建了一个小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
Run Code Online (Sandbox Code Playgroud)

或者这里有一个更 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
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 7

find . -type f -exec grep -I -q . {} \; -print
Run Code Online (Sandbox Code Playgroud)

这将-type f在当前目录(或以下)中找到所有grep认为非空和非二进制的常规文件 ( ) 。

它用于grep -I区分二进制和非二进制文件。当检测到文件是二进制文件时,该-I标志将导致grep以非零退出状态退出。根据 ,“二进制”文件是grep包含可打印 ASCII 范围之外的字符的文件。

-q选项grep将导致如果给定的图案被发现,而无需任何发射数据到它与一个零退出状态退出。我们使用的模式是一个单点,它将匹配任何字符。

如果发现文件是非二进制文件,并且至少包含一个字符,则打印文件名。

如果你觉得勇敢,你也可以插入flip -u它:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
Run Code Online (Sandbox Code Playgroud)


Wil*_*ard 5

Cas 的答案很好,但它假设文件名正常;特别是假设文件名不包含换行符。

\n\n

没有充分的理由在这里做出这个假设,因为正确处理这种情况也非常简单(在我看来实际上更干净):

\n\n
find . -type f -exec sh -c \'file "$1" | grep -q "ASCII text"\' sh {} \\; -exec flip -u {} \\;\n
Run Code Online (Sandbox Code Playgroud)\n\n

find命令仅使用POSIX 指定的功能。使用-exec作为布尔测试运行任意命令是简单的、健壮的(正确处理奇数文件名),并且比-print0.

\n\n

事实上,命令的所有部分都由 POSIX 指定,除了flip.

\n\n

请注意,这file并不能保证其返回结果的准确性。然而,实际上,在其输出中 grep 查找“ASCII 文本”是相当可靠的。

\n\n

(它可能会丢失一些文本文件,但不太可能错误地将二进制文件识别为“ASCII 文本”并破坏它\xe2\x80\x94,因此我们谨慎行事。)

\n