[英]Linux command: How to 'find' only text files?
从谷歌搜索了几次后,我想出的是:
find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text
这非常不方便,并且会输出不需要的文本,例如 mime 类型信息。 有什么更好的解决方案吗? 我在同一个文件夹中有很多图像和其他二进制文件,其中有很多我需要搜索的文本文件。
我知道这是一个古老的线程,但我偶然发现了它,并想我会分享我的方法,我发现是使用一个非常快速的方式find
找到唯一的非二进制文件:
find . -type f -exec grep -Iq . {} \; -print
grep 的-I
选项告诉它立即忽略二进制文件和.
选项与-q
将使其立即匹配文本文件,因此运行速度非常快。 如果您担心空格,您可以将-print
更改为-print0
以管道到xargs -0
或其他内容(感谢您的提示,@lucas.werkmeister!)
此外,第一个点仅对于某些 BSD 版本的find
是必需的,例如在 OS X 上,但如果您想将它放在别名或其他东西中,它不会伤害任何东西。
编辑:正如@ruslan 正确指出的那样, -and
可以省略,因为它是隐含的。
基于这个问题:
grep -rIl "needle text" my_folder
为什么不方便? 如果您需要经常使用它,并且不想每次都输入它,只需为它定义一个 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"
只要你愿意。
编辑以反映 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
}
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" "%"
这样做的另一种方法:
# find . |xargs file {} \; |grep "ASCII text"
如果你也想要空文件:
# find . |xargs file {} \; |egrep "ASCII text|empty"
我对 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 个文件)的 4 秒相比,这仅需要 0.2 秒,即快 20 倍。
此外,没有人将ag、Silver Searcher或ack-grep列为替代方案。 如果其中之一可用,它们是更好的选择:
ag -t "needle text" # Much faster than ack
ack -t "needle text" # or ack-grep
最后一点,请注意误报(将二进制文件作为文本文件)。 我已经使用 grep/ag/ack 得到了误报,所以最好在编辑文件之前先列出匹配的文件。
这个怎么样:
$ 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|:[^:]*$||'
这是我如何做到的......
1 . 编写一个小脚本来测试文件是否为纯文本 istext:
#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]
2 . 像以前一样使用 find
find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;
虽然这是一个老问题,但我认为下面的信息会增加这里答案的质量。
当忽略设置了可执行位的文件时,我只使用以下命令:
find . ! -perm -111
为了防止它递归进入其他目录:
find . -maxdepth 1 ! -perm -111
不需要管道来混合大量命令,只需强大的普通find命令。
也就是说,我希望这对任何人都有用。
我是这样做的: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"
哈:)
我更喜欢 xargs
find . -type f | xargs grep -I "needle text"
如果您的文件名很奇怪,请使用 -0 选项查找:
find . -type f -print0 | xargs -0 grep -I "needle text"
grep eth0 $(find /etc/ -type f -exec file {} \\; | egrep -i "text|ascii" | cut -d ':' -f1)
这是一个简化版本,并为像我这样试图学习如何在一行中放置多个命令的初学者提供了扩展解释。
如果您要分步写出问题,它将如下所示:
// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename
为此,我们可以使用三个 UNIX 命令: find
、 file
和grep
。
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'
| grep 'ASCII'
= |
是一个管道。 管道将左边任何东西的输出用作右边任何东西的输入。 它获取find
命令的输出(一个字符串,它是单个文件的文件类型)并测试它是否包含字符串'ASCII'
。 如果是,则返回 true。
现在,当grep
命令返回 true 时, find ./
右侧的表达式将返回 true。 瞧。
如果您有兴趣使用令人敬畏的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 提示符
您可以修改&&
之后的部分以调用其他脚本或内联执行其他一些操作,即如果该文件包含给定的字符串,则对整个文件进行分类或在其中查找辅助字符串。
解释:
find
作为文件的项目xargs
将每个项目作为一行输入到一个班级bash
命令/脚本中file
通过魔术字节检查file
类型, grep
检查 ASCII 是否存在,如果存在,则在&&
您的下一个命令执行之后。find
打印结果为null
分隔,这很好地转义包含空格和元字符的文件名。xargs
,使用-0
选项,读取它们以null
分隔, -I @@
获取每条记录并用作位置参数/args 到 bash 脚本。--
for bash
确保它后面的任何内容都是一个参数,即使它以-
像-c
开头,否则可能会被解释为 bash 选项如果您需要查找 ASCII 以外的类型,只需将grep ASCII
替换为其他类型,例如grep "PDF document, version 1.4"
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'
使用 find 命令列出所有文件,使用 file 命令验证它们是文本(不是 tar,key),最后使用 awk 命令过滤并打印结果。
这个怎么样
find . -type f|xargs grep "needle text"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.