【问题标题】:Linux command: How to 'find' only text files?Linux 命令:如何仅“查找”文本文件?
【发布时间】:2011-06-13 15:15:27
【问题描述】:

在谷歌搜索了几次后,我想出的是:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

这很不方便,会输出不需要的文本,例如 mime 类型信息。有更好的解决方案吗?我在同一个文件夹中有很多图像和其他二进制文件,还有很多我需要搜索的文本文件。

【问题讨论】:

    标签: linux search find


    【解决方案1】:

    我知道这是一个旧线程,但我偶然发现了它并想分享我的方法,我发现这是一种非常快速的方法,可以使用 find 仅查找非二进制文件:

    find . -type f -exec grep -Iq . {} \; -print
    

    grep 的-I 选项告诉它立即忽略二进制文件,. 选项连同-q 将使它立即匹配文本文件,因此运行速度非常快。如果您担心空格,可以将-print 更改为-print0 以通过管道连接到xargs -0 或其他内容(感谢@lucas.werkmeister 的提示!)

    此外,第一个点仅对某些 BSD 版本的 find 是必需的,例如在 OS X 上,但如果您想将它放在别名或其他东西中,它不会伤害任何东西。

    编辑:正如@ruslan 正确指出的那样,-and 可以省略,因为它是隐含的。

    【讨论】:

    • 在 Mac OS X 上,我需要将其更改为 find . -type f -exec grep -Il "" {} \;
    • 这比 peoro 的回答要好,因为 1. 它实际上回答了问题 2. 它不会产生误报 3. 它的性能更高
    • 你也可以使用find -type f -exec grep -Iq . {} \; -and -print,它的好处是将文件保存在find;您可以将-print 替换为另一个仅针对文本文件运行的-exec。 (如果你让grep 打印文件名,你将无法区分文件名中带有换行符。)
    • @NathanS.Watson-Haigh 它不应该,因为它应该立即匹配文本文件。您有可以分享的具体用例吗?
    • find . -type f -exec grep -Il . {} + 更快。缺点是它不能像@lucas.werkmeister 建议的那样被另一个-exec 扩展
    【解决方案2】:

    基于this SO question

    grep -rIl "needle text" my_folder

    【讨论】:

    • 谢谢,-I 是救生员。
    【解决方案3】:

    为什么不方便?如果您需要经常使用它,并且不想每次都键入它,只需为其定义一个 bash 函数即可:

    function findTextInAsciiFiles {
        # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
        find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
    }
    

    把它放在你的.bashrc 然后运行:

    findTextInAsciiFiles your_folder "needle text"
    

    随时随地。


    EDIT 以反映 OP 的编辑:

    如果您想删除 mime 信息,您可以在管道中添加一个进一步的阶段来过滤掉 mime 信息。这应该可以解决问题,只取::cut -d':' -f1:

    function findTextInAsciiFiles {
        # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
        find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
    }
    

    【讨论】:

    • 我不确定“grep text”是否足够准确以获取所有文本文件 - 我的意思是,是否有任何文本文件类型在其 mime 类型的字符串中没有“文本”说明?
    • @kavoir.com:是的。来自file 手册:“用户依赖于知道目录中的所有可读文件都打印了‘text’这个词。”
    • 在 grepping 之前搜索文本文件,而不是 grepping 然后过滤掉文本文件不是更聪明吗?
    • /proc/meminfo/proc/cpuinfo 等是文本文件,但file /proc/meminfo 表示/proc/meminfo: empty。我想知道除了“文本”之外是否应该测试“空”,但不确定其他类型是否也可以报告“空”。
    • “为什么不方便?” - “输出不需要的文本”。这个答案不能解决这个问题。
    【解决方案4】:
    find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"
    

    不幸的是,这不是节省空间。将其放入 bash 脚本中会更容易一些。

    这是空间安全的:

    #!/bin/bash
    #if [ ! "$1" ] ; then
        echo "Usage: $0 <search>";
        exit
    fi
    
    find . -type f -print0 \
      | xargs -0 file \
      | grep -P text \
      | cut -d: -f1 \
      | xargs -i% grep -Pil "$1" "%"
    

    【讨论】:

    • 您的脚本中有几个问题: 1. 如果一个二进制文件被命名为text.bin 怎么办? 2.如果文件名包含:怎么办?
    【解决方案5】:

    另一种方法:

    # find . |xargs file {} \; |grep "ASCII text"
    

    如果你也想要空文件:

    #  find . |xargs file {} \; |egrep "ASCII text|empty"
    

    【讨论】:

      【解决方案6】:

      这个怎么样:

      $ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'
      

      如果您想要没有文件类型的文件名,只需添加最后一个 sed 过滤器。

      $ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'
      

      您可以通过在最后一个grep 命令中添加更多-e 'type' 选项来过滤掉不需要的文件类型。

      编辑:

      如果你的xargs 版本支持-d 选项,上面的命令就变得更简单了:

      $ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'
      

      【讨论】:

      • 愚蠢的我。没有注意到递归grep。据我了解,即使在许多应用程序中有点受限,它实际上也非常快。为你 +1。
      【解决方案7】:

      这就是我的做法......

      1 .制作一个小脚本来测试文件是否为纯文本 文本:

      #!/bin/bash
      [[ "$(file -bi $1)" == *"file"* ]]
      

      2 。像以前一样使用查找

      find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;
      

      【讨论】:

      • 我猜你的意思是== *"text"* ]]
      • 你可以使用匹配操作符 `=~ "text" ]]` 代替。
      【解决方案8】:

      我对 histumness 的回答有两个问题:

      • 它只列出文本文件。它实际上并没有将它们搜索为 请求。要实际搜索,请使用

        find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
        
      • 它为每个文件生成一个 grep 进程,这非常慢。那么更好的解决方案是

        find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
        

        或者干脆

        find . -type f -print0 | xargs -0 grep -I "needle text"
        

        与上述解决方案(2.5GB 数据/7700 个文件)相比,这只需要 0.2 秒,即 快 20 倍

      此外,没有人引用 ag, the Silver Searcherack-grep¸ 作为替代方案。如果其中之一可用,它们是更好的选择:

      ag -t "needle text"    # Much faster than ack
      ack -t "needle text"   # or ack-grep
      

      最后一点,谨防误报(将二进制文件视为文本文件)。我已经使用 grep/ag/ack 误报了,所以最好在编辑文件之前先列出匹配的文件。

      【讨论】:

        【解决方案9】:

        对于像我这样正在尝试学习如何将多个命令放在一行中的初学者来说,这是一个带有扩展解释的简化版本。

        如果你要分步写出问题,它应该是这样的:

        // For every file in this directory
        // Check the filetype
        // If it's an ASCII file, then print out the filename
        

        为此,我们可以使用三个 UNIX 命令:findfilegrep

        find 将检查目录中的每个文件。

        file 会给我们文件类型。在我们的例子中,我们正在寻找“ASCII 文本”的返回

        grep 将在file 的输出中查找关键字“ASCII”

        那么我们怎样才能将它们串在一行中呢?有多种方法可以做到这一点,但我发现按照我们的伪代码的顺序来做最有意义(尤其是对于像我这样的初学者)。

        find ./ -exec file {} ";" | grep 'ASCII'

        看起来很复杂,但分解后还不错:

        find ./ = 查看此目录中的每个文件。 find 命令打印出与“表达式”匹配的任何文件的文件名,或者路径后面的任何内容,在我们的例子中是当前目录或./

        要理解的最重要的事情是,第一个位之后的所有内容都将被评估为 True 或 False。如果为 True,文件名将被打印出来。如果不是,则命令继续。

        -exec = 这个标志是 find 命令中的一个选项,它允许我们使用其他命令的结果作为搜索表达式。这就像在函数中调用函数。

        file {} = 在find 内部调用的命令。 file 命令返回一个字符串,告诉您文件的文件类型。通常,它看起来像这样:file mytextfile.txt。在我们的例子中,我们希望它使用find 命令正在查看的任何文件,因此我们放入花括号{} 以充当空变量或参数。换句话说,我们只是要求系统为目录中的每个文件输出一个字符串。

        ";" = 这是find 所要求的,并且是我们-exec 命令末尾的标点符号。如果需要,请通过运行man find 查看“查找”手册以获取更多说明。

        | grep 'ASCII' = | 是一个管道。管道获取左侧任何内容的输出并将其用作右侧任何内容的输入。它获取find 命令的输出(一个字符串,它是单个文件的文件类型)并测试它是否包含字符串'ASCII'。如果是,则返回 true。

        现在,find ./ 右侧的表达式将在 grep 命令返回 true 时返回 true。瞧。

        【讨论】:

          【解决方案10】:

          虽然这是一个老问题,但我认为下面的信息将增加这里答案的质量。

          当忽略设置了可执行位的文件时,我只使用这个命令:

          find . ! -perm -111
          

          为了防止它递归进入其他目录:

          find . -maxdepth 1 ! -perm -111
          

          pipes 不需要混合很多命令,只需强大的普通 find 命令即可。

          • 免责声明:这不是 OP 要求的确切,因为它不检查文件是否为 二进制。例如,它会过滤掉 bash 脚本 文件,这些文件本身就是 文本,但设置了 可执行位

          也就是说,我希望这对任何人都有用。

          【讨论】:

            【解决方案11】:

            我是这样做的: 1)由于要搜索的文件太多(〜30k),我每天使用以下命令生成文本文件列表以通过crontab使用:

            find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &
            

            2) 在.bashrc中创建一个函数:

            findex() {
                cat ~/.src_list | xargs grep "$*" 2>/dev/null
            }
            

            然后我可以使用以下命令进行搜索:

            findex "needle text"
            

            HTH:)

            【讨论】:

              【解决方案12】:

              我更喜欢 xargs

              find . -type f | xargs grep -I "needle text"
              

              如果您的文件名很奇怪,请使用 -0 选项查找:

              find . -type f -print0 | xargs -0 grep -I "needle text"
              

              【讨论】:

                【解决方案13】:
                • 在所有 text/ascii 文件中的 /etc 中搜索文本“eth0”的 bash 示例

                grep eth0 $(find /etc/ -type f -exec file {} \; | egrep -i "text|ascii" | cut -d ':' -f1)

                【讨论】:

                  【解决方案14】:

                  如果您有兴趣使用强大的file 实用程序和find 的强大功能通过其神奇字节查找任何文件类型,这可以派上用场:

                  $ # Let's make some test files
                  $ mkdir ASCII-finder
                  $ cd ASCII-finder
                  $ dd if=/dev/urandom of=binary.file bs=1M count=1
                  1+0 records in
                  1+0 records out
                  1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
                  $ file binary.file
                  binary.file: data
                  $ echo 123 > text.txt
                  $ # Let the magic begin
                  $ find -type f -print0 | \
                      xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@
                  

                  输出:

                  file is ASCII: ./text.txt
                  

                  图例:$ 是我们输入命令的交互式 shell 提示符

                  您可以修改&amp;&amp; 之后的部分以调用其他脚本或内联执行其他操作,即如果该文件包含给定字符串,则对整个文件进行分类或在其中查找辅助字符串。

                  说明:

                  • find 文件项目
                  • 使xargs 将每个项目作为一行放入一个衬里bash 命令/脚本
                  • file 通过魔术字节检查文件类型,grep 检查是否为 ASCII 如果存在,则在&amp;&amp; 之后执行您的下一个命令。
                  • find 打印结果 null 分隔,这很好逃避 包含空格和元字符的文件名。
                  • xargs ,使用 -0 选项,读取它们 null 分隔,-I @@ 获取每条记录并用作 bash 的位置参数/args 脚本。
                  • -- for bash 确保它之后的任何内容都是一个论点,甚至 如果它以- 开头,例如-c,否则可能会被解释 作为 bash 选项

                  如果您需要查找 ASCII 以外的类型,只需将 grep ASCII 替换为其他类型,例如 grep "PDF document, version 1.4"

                  【讨论】:

                    【解决方案15】:
                    find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'
                    

                    使用 find 命令列出所有文件,使用 file 命令验证它们是文本(不是 tar,key),最后使用 awk 命令过滤并打印结果。

                    【讨论】:

                      【解决方案16】:

                      这个怎么样

                       find . -type f|xargs grep "needle text"
                      

                      【讨论】:

                      • 这不寻找"needle text"
                      • @Navi:提供的示例 OP 仅查找包含 "needl text" 的文件
                      • @Navi:现在它不再查找文本文件:如果二进制文件包含"needle text",它将被找到
                      • 我为什么要听你的?
                      • @Navi:您的单行程序不检查文件类型,并且文件名中的空格也存在重大问题......
                      猜你喜欢
                      • 2023-02-16
                      • 2018-11-30
                      • 2016-05-20
                      • 1970-01-01
                      • 2011-08-07
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-09-13
                      相关资源
                      最近更新 更多