![如何在多个文件中搜索字符串并列出所有出现的文件名](https://linux22.com/image/219835/%E5%A6%82%E4%BD%95%E5%9C%A8%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B8%AD%E6%90%9C%E7%B4%A2%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%B9%B6%E5%88%97%E5%87%BA%E6%89%80%E6%9C%89%E5%87%BA%E7%8E%B0%E7%9A%84%E6%96%87%E4%BB%B6%E5%90%8D.png)
目录中有许多文件(来自 Java 应用程序的日志 - 每行一个记录),我想列出包含搜索字符串的所有文件名,并列出该字符串在特定文件中的所有出现情况。
到目前为止我一直在使用这个。在这里我得到文件名,下面我得到匹配的行,但我不知道文件名匹配的行来自哪个文件名。
#!/bin/bash
cd ${DIRECTORY}
clear
echo 'WARNINGS'
egrep -l "WARN" * | sort
echo ''
cat * | grep 'WARN'
我需要列出所有文件名(但仅限于包含 WARN 的文件名),并且对于所有这些匹配的文件,显示特定文件中带有 WARN 字符串的所有行。
答案1
一个鲜为人知的事实是,grep
当搜索多个文件时,它可以打印它(成功)搜索的文件的名称。这意味着,如果您将其包含/dev/null
到要搜索的文件列表中,您将得到您想要的内容:
$ grep "something" /path/to/file
something (and some other thing)
然而:
$ grep "something" /dev/null /path/to/file
/path/to/file:something (and some other thing)
使用它,您可以执行以下操作(的变体):
$ find /path/to/start [-name "<filename pattern>"] \
-exec grep '<searchstr>' /dev/null {} \;
这会给你这样的输出:
/path/to/start/file1:<searchstr> foo
/path/to/start/file1:<searchstr> bar
/path/to/start/subdir/file2:foo <searchstr> bar
/path/to/start/subdir/file3:bar <searchstr> foo
...
正如 @ilkkachu 正确指出的那样,我给出的命令将一次将一个文件传递到grep
.如果要搜索许多文件,这对系统来说可能是相当繁重的。优化该过程的一种方法是这样写:
$ find /path/to/start [-name "<filename pattern>"] \
-exec grep '<searchstr>' /dev/null {} +
wherefind
一次将多个文件名传递给 grep,从而产生较小的负载,因为 grep 不那么频繁地调用。您仍然需要指定,/dev/null
因为有可能find
只找到一个文件。
答案2
当您向 提供多个文件时grep
,它会将文件名附加到输出的每一行。另外,这样,您就可以避免猫的经典无用用途。当您使用它时,请避免对 shell 变量名称使用大写字母,因为按照惯例,全局环境变量名称都是大写的,因此如果您也将自己的 shell 变量大写,这可能会导致命名冲突和错误。最后,egrep
已被弃用grep -E
(但您在这里不需要grep -E
),并且在运行其中的内容之前不需要进入目录,但如果这样做,您还应该在执行任何操作之前cd
确保工作正常。cd
这是脚本的改进版本,可以满足您的需求:
#!/bin/sh
clear
printf 'WARNINGS found in files in directory "%s":\n' "$1"
grep 'WARN' "$1"/*
您现在可以运行脚本,并将目标目录名称作为参数:
your_script /path/to/target
例如,/home/terdon/foo
在我的系统上指定的目录上运行它会给出:
WARNINGS found in files in directory "/home/terdon/foo":
/home/terdon/foo/file2:WARNING from file2
/home/terdon/foo/file4:WARNING from file4
/home/terdon/foo/file4:WARNING2 from file4
/home/terdon/foo/file5:WARNING from file5
如果您只想显示文件名而不显示路径,您能选择cd
(但如上所述,如果失败请确保退出cd
):
#!/bin/sh
clear
if cd -- "$1"; then
:
else
echo "cd to '$1' failed!"
exit 1
fi
printf 'WARNINGS found in files in directory "%s":\n' "$1"
grep 'WARN' *
或者您可以从输出中删除路径:
#!/bin/sh
clear
printf 'WARNINGS found in files in directory "%s":\n' "$1"
grep 'WARN' "$1"/* | sed 's|.*/||'
最后,如果您想确保即使目标目录只有一个文件也能正常工作,如果您使用的是 GNU grep(Linux 上的默认设置),您可以使用-H
告诉 grep 始终包含文件名的标志:
#!/bin/sh
clear
printf 'WARNINGS found in files in directory "%s":\n' "$1"
grep -H 'WARN' "$1"/* | sed 's|.*/||'
如果您grep
不支持-H
,请使用巴枯宁的诡计其中包括/dev/null
:
#!/bin/sh
clear
printf 'WARNINGS found in files in directory "%s":\n' "$1"
grep 'WARN' "$1"/* /dev/null | sed 's|.*/||'
答案3
更好的利用ack
在你的Java
存储库中。
这将防止在目录或类似目录中搜索.git
,因此它更好、更快。
有更多的选择可以比grep
这种情况更好。
尝试 (递归地):
ack WARN
ack -l WARN