【问题标题】:How to count all the human readable files in Bash?如何计算 Bash 中所有人类可读的文件?
【发布时间】:2012-09-29 15:31:21
【问题描述】:

我正在参加 UNIX 入门课程,并且有一个家庭作业问题如下:

上一题有多少个文件是文本文件?文本文件是包含人类可读内容的任何文件。 (技巧问题。在文件上运行 file 命令以查看该文件是文本文件还是二进制数据文件!如果您只是计算带有 .txt 扩展名的文件的数量,您将不会得到此问题的分数。)

上一个问题只是问有多少常规文件,通过find . -type f | wc -l很容易弄清楚。

我只是无法确定“人类可读内容”是什么,因为我假设它意味着除了二进制/汇编之外的任何内容,但我认为这就是 -type f 显示的内容。也许这就是教授所说的“技巧问题”的意思?

这个问题稍后有一个跟进,它还询问“哪些文本文件包含大小写混合的字符串“csc”?”。显然“文本”不仅仅指.txt 文件,但我需要弄清楚第一个问题来确定这一点!

【问题讨论】:

  • 不,find -type f 不包括目录、设备节点等(“特殊文件”),即纯文件以外的文件系统对象。它根本不检查文件的内容,只检查目录条目(inode)中的类型信息。 Unix 对“二进制”和“文本”文件没有任何区别(对于这个 Unix-head,任何定义这种区别的尝试都会进入滑坡)。

标签: bash file unix human-readable


【解决方案1】:

为清楚起见添加了引号:

对文件运行“file”命令,查看文件是文本文件还是二进制数据文件!

file 命令将检查文件并告诉您它们看起来是什么类型的文件。 “文本”一词将(几乎)始终出现在文本文件的描述中。

例如:

desktop.ini:   Little-endian UTF-16 Unicode text, with CRLF, CR line terminators
tw2-wasteland.jpg: JPEG image data, JFIF standard 1.02

所以第一部分是要求您运行file 命令并解析其输出。

我只是无法确定“人类可读的内容”是什么,因为我假设它意味着除了二进制/汇编之外的任何内容,但我认为这就是 -type f 显示的内容。

find -type f 查找文件。它过滤掉其他文件系统对象,如目录、符号链接和套接字。不过,它会匹配任何类型的文件:二进制文件、文本文件等等。

也许这就是教授所说的“技巧问题”的意思?

听起来他只是在说不要执行find -name '*.txt' 或类似的命令来查找文本文件。不要假设特定的文件扩展名。文件扩展名在 UNIX 中的意义远小于在 Windows 中的意义。很多文件甚至没有文件扩展名!


我认为教授希望我们能够对所有文件运行文件命令并计算其中包含“文本”的文件的数量。

多部分答案怎么样?我将在#1 中给出简单的解决方案,这可能是您的教授正在寻找的。如果您有兴趣,我会解释它的缺点以及如何改进它。

  1. 一种方法是使用xargs,如果您已经了解的话。 xargs 运行另一个命令,使用来自标准输入的数据作为该命令的参数。

    $ find . -type f | xargs file
    ./netbeans-6.7.1.desktop: ASCII text
    ./VMWare.desktop:         a /usr/bin/env xdg-open script text executable
    ./VMWare:                 cannot open `./VMWare' (No such file or directory)
    (copy).desktop:           cannot open `(copy).desktop' (No such file or directory)
    ./Eclipse.desktop:        a /usr/bin/env xdg-open script text executable
    
  2. 这行得通。有点。对于家庭作业来说已经足够了。但对于现实世界的脚本来说还不够好。

    请注意它是如何在文件VMWare (copy).desktop 上中断的,因为其中有一个空格。这是由于xargs 将参数拆分为空格的默认行为。我们可以通过使用xargs -0 将命令参数拆分为 NUL 字符而不是空格来解决此问题。文件名不能包含 NUL 字符,所以这将能够处理任何事情。

    $ find . -type f -print0 | xargs -0 file
    ./netbeans-6.7.1.desktop: ASCII text
    ./VMWare.desktop:         a /usr/bin/env xdg-open script text executable
    ./VMWare (copy).desktop:  a /usr/bin/env xdg-open script text executable
    ./Eclipse.desktop:        a /usr/bin/env xdg-open script text executable
    
  3. 这对于制作脚本来说已经足够好了,而且您会经常遇到这种情况。但我个人更喜欢不需要管道的替代语法,因此效率略高。

    $ find . -type f -exec file {} \;
    ./netbeans-6.7.1.desktop: ASCII text
    ./VMWare.desktop:         a /usr/bin/env xdg-open script text executable
    ./VMWare (copy).desktop:  a /usr/bin/env xdg-open script text executable
    ./Eclipse.desktop:        a /usr/bin/env xdg-open script text executable
    

    为了理解这一点,-exec 反复调用file,将{} 替换为它找到的每个文件名。分号\; 标志着file 命令的结束。

【讨论】:

  • 当然,“人类可读”是人类而非文件的功能。例如,我一直有一个强烈的印象,大多数名为 README 的文件实际上并不被大多数人阅读。
  • 感谢帮助,我知道文件可以确定文件是否为“文本”文件,但我忘了提到第一个关于查找常规文件的问题的最终结果在 153 个文件中(它是一个充满了我们正在使用的子目录的目录),所以我认为教授希望我们能够对所有文件运行 file 命令并计算其中包含“文本”的文件的数量.假设有可能,我将如何去做?
  • @user1687406 更新了我的答案,对结合findfile 进行了(过于)详细的解释。我没有讨论如何grep 输出。如果您需要这方面的帮助,请告诉我。
【解决方案2】:

有一种很好且简单的方法可以确定文件是否是人类可读的文本文件,只需使用file --mime-type <filename> 并查找'text/plain'。无论文件是否具有结尾或与 .txt 不同的结尾,它都会起作用

所以你会这样做:

FILES=`find $YOUR_DIR -type f`

for file in $FILES ;
do

mime=`/usr/bin/file --mime-type $YOUR_DIR/$file | /bin/sed 's/^.* //'`

if [ $mime = "text/plain" ];  then      
    fileTotal=$(( fileTotal + 1 ))
    echo "$fileTotal - $file"
fi

done

echo "$fileTotal human readable files found!"

输出会是这样的:

1 - /sampledir/samplefile
2 - /sampledir/anothersamplefile
....
23 human readable files found!

如果您想进一步了解更多人类可读的 mime 类型(例如,HTML 和/或 XML 算不算?),请查看 http://www.feedforall.com/mime-types.htm

【讨论】:

    猜你喜欢
    • 2013-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 2017-06-06
    • 2015-09-28
    • 1970-01-01
    相关资源
    最近更新 更多