我有一个包含超过 150K 文件的目录。我想创建一个包含存储在文本变量中的文本的所有文件的列表,并将该文件列表存储在另一个变量中。
我首先尝试:
searchtext="Subject: Your"
files = $(grep "$searchtext" ./* | awk '{print ($1)}' )
,虽然这适用于目录中的中等数量的文件,但在包含 150K 文件的目录上运行时,它会生成错误“参数列表太长”。 (带 print 的 awk 用于从 grep 结果中仅提取文件名。)
我发现 files=$(grep "$searchtext" ./* | awk '{print ($1)}') 适用于 150K 文件目录,但运行时间几乎需要 90 分钟。
如果文件中存在,$searchtext 字符串将位于文件的开头。所以我想如果 grep 仅限于显示前 30 行文本,我可以大大加快速度。我不确定如何做到这一点,我发现如何递归地 grep 目录中每个文件的前 50 行?并尝试了那里的一些建议。最适合我的任务的是:
searchtext="Subject: Your"
find . -type f -exec head -n 30 {} + | grep "$searchtext"
这在可接受的时间内运行,但它不会输出包含搜索文本的文件的文件名。我尝试了 grep -l,但这会导致错误:“find: head' terminated by signal 13 ". Somewhere it was suggested that the using "\" instead of "+" might be more appropriate. However, that also generates an error: "find: missing argument to
-exec' ”。
展望 grep 结果包含文件名时,我预计会出现另一个问题。当我尝试将 grep 输出分配给变量时:
files = $(find . -type f -exec head -n 30 {} + | grep "$searchtext")
我收到错误“ut1.sh:第 16 行:文件:找不到命令”。由于某种原因,变量“files”被解释为命令?我的脚本名称是 ut1.sh 。我之前已经多次以这种方式分配变量,没有出现问题。
我的 bash 版本是 GNU bash,版本 4.1.2(2)-release (x86_64-redhat-linux-gnu)
如何完成工作,我的尝试出了什么问题?
谢谢
答案1
要获取匹配的文件名列表grep
,您可以使用-l
开关仅获取文件名,无需使用awk
来处理输出。在匹配文件的情况下,这也更快,因为grep
可以在找到模式一次后停止。
grep -le "$searchtext" ./*
您可以通过简单的赋值将其输出放入变量中(但带有空格和通配符的文件名会导致问题):
files=$(grep -le "$searchtext" ./* )
至于这个:
find . -type f -exec head -n 30 {} + | grep "$searchtext"
这里的管道将find
和分开grep
,因此您可以有效地连接每个文件的前 30 行(此处丢失文件名的跟踪),然后 grep 结果。grep -l
只能告诉您整个输入中是否有任何匹配项。您需要从内部运行一个 shellfind
来单独组合每个文件的head
和grep
:
export searchtext
find . -type f -exec sh -c 'head -n 30 "$1" | grep -q "$searchtext" && echo "$1"' sh {} \;
但我们不妨awk
这样做。这将仅在前 30 行查找模式 (GNU awk):
awk -vpattern="$searchtext" 'FNR <= 30 && $0 ~ pattern { print FILENAME; nextfile }' *
或使用查找:
find . -type f -exec awk -vpattern="$searchtext" 'FNR <= 4 && $0 ~ pattern { print FILENAME; nextfile }' {} +
答案2
对于bash
4.4+ 和 GNU grep
:
readarray -td '' files < <(grep -rZFle "$searchtext" .)
如果是电子邮件文件,您可能只想在此处的标题中搜索,因为您似乎正在寻找主题。使用 GNU awk
:
readarray -td '' files < <(
SEARCH="$searchtext" find . -type f -exec gawk -v ORS='\0' -v RS='\r?\n' '
$0 == "" {nextfile}
index($0, ENVIRON["SEARCH"]) {print FILENAME; nextfile}' {} +
)