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


43

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

我已经从Windows用户那里收到了文件存档。该档案包含源代码和图像文件。我们的构建系统不适用于带有Windows行尾的文件。我有一个命令行程序(flip -u),它将在* nix和Windows之间翻转行尾。所以,我想做这样的事情

find . -type f | xargs flip -u

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

那么,有没有办法在目录树中找到所有非二进制文件?还是我应该考虑其他解决方案?


1
您可以file在脚本/管道中的某处使用该实用程序,以识别文件是数据还是文本
lk- 2012年

1
您所说的非二进制是什么(现代计算机上的一切都是二进制)。我猜您正在使用与具有文本和二进制文件的旧C / PM操作系统的区别。文本文件可以是任意长度,但必须以ctrl-z结尾,二进制文件必须是512byte块的倍数。如果是这样,则表示文本文件。(我还注意到,您在非二进制文件中写到行尾,这也暗示它们是文本文件)这是正确的吗?
ctrl-alt-delor

所有文件都是二进制的,这只是解释的内容。您是否要查找文本文件?
ctrl-alt-delor

@richard我进入了一个时代,我们将文件解释为纯文本纯文本,并将所有其他文件(图像,文字处理文档等)称为二进制文件。我知道引擎盖下的全是零:)
艾伦·斯托姆

1
啊,我明白了您对我的用语的意思-以后我会使用二进制/文本来避免混淆。回复:\ r \ n的事情-我理解这些是打字机回车符(移至行首)和换行符(向下移行)的ASCII字符。因此\ r \ n是行末字符用于现实世界的“更准确”的模型。在OS X之前的版本中,Mac仅使用\ r。我通常将整个事情记为“我们仍在处理的匆忙中做出的任意选择”
艾伦·斯托姆

Answers:


20

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

就像是:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

请注意,grep会搜索“ ASCII文本”而不​​是任何“文本”-您可能不希望将Rich Text文档或unicode文本文件等弄乱。

您还可以使用find(或其他方式)生成要检查的文件列表file

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

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

请注意,换行符是文件名中的有效字符,因此,如果任何文件名中包含换行符,则换行符将中断。对于典型的unix用户,从病理上来说这是疯狂的,但是并不是文件是否起源于Mac或Windows计算机。

另请注意,这file并不完美。它非常适合检测文件中的数据类型,但有时会引起混淆。

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


1
感谢您的解决方案!由于某些原因而不是在我的Solaris系统上file显示,因此我相应地修改了该部分。另外,我用等效的替换。English textASCII textawk -F: '{print $1}'cut -f1 -d:
安德鲁昌

3
值得一提的是grep -I过滤器二进制文件
xenoterracide

寻找单词text应该足够了。这也将拿起file像描述ASCII Java program textHTML document texttroff or preprocessor input text
user1024 2016年

我的答案部分是对该答案的回应/改进。关于ASCII textgrepping的非常重要的一点是避免弄乱RTF。
2016年

1
xenoterracide:您救了我的命男!只是一个标志-I和BINGO
塞尔吉奥·阿布鲁

9

不。二进制文件或非二进制文件没有什么特别的。您可以使用“仅包含0x01–0x7F中的字符”之类的试探法,但这将调用具有非ASCII字符二进制文件的文本文件和不幸运的二进制文件文本文件。

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

压缩文件

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

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

其他文件

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


6

一般解决方案只处理非二进制文件的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

6

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

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

它在实际应用中正在使用:dos2unix

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


4

Cas的答案很好,但是它假定文件名合理。特别是,假定文件名将不包含换行符。

这里没有充分的理由做此假设,因为正确地处理该情况也很简单(我认为实际上更干净):

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

find命令仅使用POSIX指定的功能。使用-exec作为布尔测试运行任意命令是简单,鲁棒(正确处理奇文件名),以及比更便携-print0

实际上,除之外,命令的所有部分均由POSIX指定flip

请注意,file这并不保证其返回结果的准确性。但是,实际上,在输出中对“ ASCII文本”进行grepping是非常可靠的。

(它可能会丢失一些文本文件,但是非常不可能将二进制文件错误地标识为“ ASCII文本”并对其进行处理-因此我们谨慎行事。)


无参数的文件calls可能会非常慢,例如,对于视频,它将告诉您有关编码的所有信息。
phk

另外,您假设没有文件以开头-
phk

而且我认为没有理由不只file对它进行一次调用,它可以将多个文件作为参数。
phk

@phk,请发表您的评论:(1)知道潜在的慢点是一件好事,但是我发现没有POSIX方法可以防止这种情况;(2)我对文件名假设为零,因为该find命令将以前缀./形式传递给shell命令;(3)我一次只能看到一次grep对单个file命令输出进行测试,以确保正确处理可能包含换行符的文件名的POSIX方法。
通配符

我查看了您最终的“ POSIX-y”解决方案,我认为它很聪明,但是您认为它file支持--mime-encoding标志和--分隔符,而POSIX都不保证
2016年

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 {} \;

1

尝试这个 :

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

的论点grep '[^ -~]''[^<tab><space>-~]'

如果在shell命令行上键入它,请在前面输入Ctrl+ 。在编辑器中,应该没有问题。VTab

  • '[^<tab><space>-~]'将匹配非ASCII文本的任何字符(回车符将被忽略grep)。
  • -L 将仅打印不匹配文件的文件名
  • -Z将输出以空字符分隔的文件名(用于xargs -0

值得注意的是,可以使用类似Perl的Regex grep -P(如果可用)\t。另外,使用的语言环境翻译,如果外壳支持它:$'\t'bashzsh做)。
phk

1

替代解决方案:

dos2unix命令会将行尾从Windows CRLF转换为Unix LF,并自动跳过二进制文件。我使用以下方法递归地应用它:

find . -type f -exec dos2unix {} \;

由于dos2unix可以使用多个文件名作为参数,所以这样做的效率要高得多find . -type f -exec dos2unix {} +
Anthon

0

sudo find /(- type f -and -path'* / git / *'-iname'README')-exec grep -liI'100644 \ | 100755'{} \; -exec flip -u {} \;

i。(-type f -and -path'* / git / *'-iname'README'):在包含名称git的路径和名为README的文件中搜索文件。如果您知道要搜索的任何特定文件夹和文件名,将很有用。

ii.-exec命令在由find生成的文件名上运行命令

iii。\; 指示命令结束

iv。{}是从先前的查找搜索中找到的文件/文件夹名称的输出

v。随后可以运行多个命令。通过附加-exec“ command” \; 如使用-exec flip -u \;

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

您可以克隆此测试目录并进行尝试:https : //github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

更详细的答案在这里:https : //github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.