递归搜索模式,然后为每个匹配打印出特定的序列:行号、文件名和无文件内容

递归搜索模式,然后为每个匹配打印出特定的序列:行号、文件名和无文件内容

我想要的内容与此处几乎完全相同,但我希望结果中的格式为“行号、分隔符、文件名、换行符”,从而在行的开头显示行号,而不是在文件名之后,并且不显示包含匹配项的行。

这种格式更可取的原因是

  • (A)文件名可能又长又神秘,并且包含该工具用来将文件名与行号分开的分隔符,这使得使用 awk 来实现这一点非常困难,因为文件内的模式也可能包含相同的分隔符。此外,行开头的行号比出现在文件名之后的行号对齐得更好。这种所需格式的另一个原因是
  • (二)与模式匹配的行可能太长,并且会弄乱标准输出上显示的输出中的每行一行属性(并且在标准输出上查看输出比必须保存到文件并使用 vi 等工具来查看要好输出文件中每行一行)。

    如何递归搜索目录中的模式并仅打印出文件名和行号

现在我已经提出了要求,请考虑以下内容:

  1. 我使用的Linux主机上没有安装Ack,所以我无法使用它。

  2. 如果我执行以下操作,shell 将执行find .并用从当前工作目录开始并递归向下进行的绝对路径列表替换“find .”:

    grep -n PATTERN $(find .)
    

    然后 -n 打印行号,但不是我想要的地方。另外,由于某种原因我不明白,如果目录名称包含模式,那么 grep 除了包含该模式的常规文件之外还匹配它。这不是我想要的,所以我使用:

    grep -n PATTERN $(find . -type f)
    

    我还想更改此命令,以便将 find 的输出动态传递给 grep。不必先构建完整的绝对路径列表,然后将其中的大部分传递给 grep,而是让 find 在构建列表时将每一行传递给 grep,所以我尝试了:

    find . -exec grep -n PATTERN  '{}' \;
    

    根据 ,这似乎是正确的语法,man page但是当我发出这个命令时,Bash shell 的执行速度慢了大约 100 倍,所以这不是正确的方法。

鉴于我所描述的内容,我如何执行类似于此命令的操作并获得所需的格式。我已经列出了与相关帖子相关的问题。

答案1

使用 grep

为什么不能只使用-rswitch togrep来递归文件系统而不是使用find?我还会使用 2 个附加开关来代替开关-n

$ grep -rHn PATTERN <DIR> | cut -d":" -f1-2

例子#1

$ grep -rHn PATH ~/.bashrc | cut -d":" -f1-2
/home/saml/.bashrc:25

细节

  • -r- 递归搜索文件+目录
  • -H- 如果文件名匹配(限制性比 少),则打印文件名,即它与的其他开关-l一起使用grep
  • -n- 显示匹配的行号

例子#2

$ grep -rHn PATH ~/.bash* | cut -d":" -f1-2
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25

使用查找

$ find . -exec sh -c 'grep -Hn PATTERN "$@" | cut -d":" -f1-2' {}  +

例子

$ find ~/.bash* -exec sh -c 'grep -Hn PATH "$@" | cut -d":" -f1-2' {}  +
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25

如果您确实想使用,find您可以执行类似的操作,grep在使用 .find 文件找到文件后执行find

答案2

grep -n PATTERN `find . -type f`

这很糟糕,因为命令替换的输出被解释为以空格分隔的文件名通配符模式列表。如果任何文件名包含空格或其中之一\[*?,则此代码段不起作用。另外,如果有很多匹配文件,最终会导致命令行太长。

find . -exec grep -n PATTERN  '{}' \;

这很好而且可靠,但grep每个文件调用一次。这就是为什么它这么慢的原因。

用于-exec … {} +批量执行尽可能多的文件的命令。请注意,最后一批(或理论上其他批次)可能由单个文件组成,因此grep不会打印文件名;传递-H选项以始终打印文件名,或添加参数/dev/null(它从不包含任何匹配项,但确保grep至少看到两个文件名)。

find . -type f -exec grep -Hn PATTERN {} +

GNU grep 没有打印匹配行号的选项,但没有打印匹配行文本的选项。您可以使用 sed 去除匹配的文本,并将行号与文件名交换。

find . -type f -exec grep -Hn PATTERN {} + | sed 's/^\([^:]*\):\([^:]*\):.*/\2:\1/'

如果您想右对齐行号,awk 比我能想到的任何替代方法都要简单得多。

find . -type f -exec grep -Hn PATTERN {} + | awk -F : '{printf "%8d:%s", $2, $1}'

您可以通过在 awk 而不是 grep 中进行匹配来获得更多控制。 Awk 往往会慢一些,因为它是一种更通用的解释性语言工具。一个好处是您可以选择如何处理包含冒号或换行符的文件名,这会导致 grep 的输出不明确。以下代码片段使用 awk 进行搜索并处理包含以下内容的文件名:(甚至换行符,但对于这些文件名,它会产生不明确的输出)。请注意 awk 使用扩展正则表达式,就像grep -E(有微小的变化,但并不比 grep 或 awk 的实现之间的变化更多)。

find . -type f -exec awk '/PATTERN/ {printf "%d:", FNR; print FILENAME}' {} +

相关内容