我目前正在尝试更多地了解 bash 脚本和所有这些有趣的东西,我拼凑了这个小命令:
find $path | xargs grep -n $pattern | awk '{print $1}'
虽然这确实有效,但我想知道我是否在重新发明轮子。有没有更好的方法来搜索目录、grep 文件中的模式并返回包含行号的列表?
答案1
许多grep
变体都实现了递归选项。例如,GNU grep
-R, -r, --recursive
Read all files under each directory, recursively; this is equivalent to the -d recurse option.
然后您可以删除find
:
grep -n -r $pattern $path | awk '{ print $1 }'
但这保留的不仅仅是行号。awk
正在打印第一列。这个例子
src/main/package/A.java:3:import java.util.Map;
src/main/package/A.java:5:import javax.security.auth.Subject;
src/main/package/A.java:6:import javax.security.auth.callback.CallbackHandler;
将被打印为
src/main/package/A.java:3:import
src/main/package/A.java:5:import
src/main/package/A.java:6:import
注意:import
每一行中的 。您可能想用来sed
过滤输出。
由于 a:
可能出现在文件名中,因此您可以使用-Z
grep 选项在文件名后输出 nul 字符 (\0)。
grep -rZn $pattern $path | sed -e "s/[[:cntrl:]]\([0-9][0-9]*\).*/:\1/"
使用与之前相同的示例将产生
src/main/package/A.java:3
src/main/package/A.java:5
src/main/package/A.java:6
答案2
对于第一部分,请注意,仅当文件名中xargs
没有空格字符时才有效。\'"
看如何在linux目录的全部内容中搜索单词寻求解释和替代方案。
另外,始终在变量替换两边加上双引号:"$path"
。如果没有双引号,shell 会扩展 值中的空格和通配符$path
,因此如果该文件名中包含空格或通配符,则使用不带引号的它会中断。这同样适用$pattern
(只是为了笑,尝试省略引号并在包含名为和h*
的文件的目录中搜索)。hi
hello
如果您的版本grep
可以-r
选择递归地遍历目录,则不需要find
这里。该-r
选项适用于 Linux、FreeBSD、Mac OS X 和 Cygwin 等。否则:
find "$path" -type f -exec grep -Hn "$pattern" {} + | awk -F: '{print $1 ":" $2}'
我awk
也修复了上面的调用,以便它仅打印文件名和行号。我还将-H
选项传递给grep
, 以确保它始终打印文件名,即使碰巧只有一个文件。此代码假设您的文件名不包含:
换行符;如果可能的话,事情就会变得复杂,你最好要么依赖 GNU grep 的-Z
选项或单独处理文件:
find "$path" -type f -exec sh -c 'for x; do grep -n "$0" <"$x" | awk -v fn="$x" -F: 'print fn ":" $1'; done' "$pattern" {} +
答案3
我会摆脱grep
并使用awk
:
find $path -type f -print0 | xargs -0 awk "/$pattern/{print FILENAME,FNR}"
但使用grep
and cut
:
find $path -type f -print0 | xargs -0 grep -nH "$pattern" | cut -d: -f1,2
包含该-type f
子句,这样您在尝试搜索(在 grep 或 awk 中)非常规文件类型(符号链接、目录、套接字)时就不会出现错误。如果您在另一个程序应该从管道或套接字读取数据时,那么您可能会弄乱该程序。
find ... -print0 | xargs -0
解决了文件名中存在空格的问题。它并非在每个 UNIX 系统上都可用,但在大多数系统上都可用。
答案4
也检查-c
和-n
有用的选项。