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

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

是否可以使用该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

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

答案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用于过滤二进制文件的小脚本,该脚本在较新版本中使用新方法,并使用参数-00file并在旧版本上回退到以前的方法:

#!/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

相关内容