使用 find -exec grep,如何仅打印基本名称或将文件名/行内容拆分为单独的行?

使用 find -exec grep,如何仅打印基本名称或将文件名/行内容拆分为单独的行?

我正在看源文件。实际上对于 PureBasic,但无论哪种语言,都涉及到共同的元素。就我而言,该命令将是 cover extensions pbpbipbfpbp

find . -name "*.pb*" -exec grep -Hnr ... \;

问题是,这可能会导致很长的行,并且过于紧凑而不易阅读,因为它们包含full/path/filename.ext:ln#:contents of that line.

我想做两件事之一:要么完全切断路径(最后一个/在第一个之前:)并离开:

filename.ext:ln#:contents of that line

或分割线,这样我就有:

full/path/filename.ext:    
ln#:contents of that line

无论哪种方式都会使结果更具可读性。您甚至可以将其拆分为您拥有的位置:

full/path/filename.ext:ln#:  
contents of that line

以提高可读性。我很灵活。它可以是其中任何一个。它甚至不必局限于一行命令或这些特定命令。

我已经尝试了我能找到的所有列出的技术,包括findgrepsed我无法让组合完全达到我想要的效果。

答案1

根据您的描述,我认为您正在这样做(我将其分成四行以避免水平滚动)

>tmp.txt;
find . -type f -name "*.pb*" \
     -exec grep -Hn pattern "{}" ";" >>tmp.txt;
cat tmp.txt | more

如果您使用 Gnu find,则可以使用;-execdir代替-exec这将导致grep命令从目标文件的目录中执行,结果是文件名将相对于它所在的目录。(即./filename.ext)。那不是相当你所要求的,但它非常接近,并且还有其他充分的理由使用-execdir.

     -execdir grep -Hn pattern "{}" ";"      # See note 1

另一种选择是使用中间 shell 来编辑文件名标签。这需要 Gnugrep选项--label

     -exec bash -c 'grep -Hn --label="$(basename "$1")" pattern "$1"' \
           _ "{}" ";"

这对于操作系统来说是相当长的时间和更多的工作,但它确实消除了./.

对于第二个选项,您可以通过管道传输整个输出sed,在每行的第一行(或第二行)之后插入换行符:

find . -type f -name "*.pb*" \
     -exec grep -Hn pattern "{}" ";" |
sed 's/:/&\n/' >>tmp.txt.

或(第二个冒号):

find . -type f -name "*.pb*" \
     -exec grep -Hn pattern "{}" ";" |
sed 's/:[^:]*:/&\n/' >>tmp.txt.

笔记

  1. 告诉 grep 进行递归搜索 ( -r) 是没有意义的,因为你永远不会给它一个目录作为参数。find已经递归地找到了所有文件。所以我从所有示例中删除了该选项。

  2. cat tmp.txt | more是猫的无用使用(UUOC)。只需使用more tmp.txt.

  3. 使用 Gnu find 替换;+以提高效率(除了使用 的示例bash -c)。

答案2

从你要寻找的前提出发图案在您的文件中,以下是针对您建议的每个布局的解决方案:

  1. 删除路径名的前导部分

    find . -name '*.pb*' -type f -execdir grep -Hn 'PATTERN' {} \; | cut -c3- >tmp.txt
    more tmp.txt
    

    当使用execdir它引用的程序时,总是从目标目录调用,并且当前文件的路径名以./.只是从路径名中cut -c3-删除前导。./

  2. 用路径名分割行,后跟行号和匹配项

    find . -name '*.pb*' -type f -exec grep -Hn 'PATTERN' {} \; | sed 's/:/:\n/' >tmp.txt
    
  3. 使用路径名和行号分割行,然后匹配

    find . -name '*.pb*' -type f -exec grep -Hn 'PATTERN' {} \; | sed 's/^\([^:]*:[^:]*:\)/\1\n/' >tmp.txt
    

    在这里,sed模式在第二个冒号处分裂(RE 读作从该行的开头开始。匹配零个或多个非冒号字符,后跟冒号。然后再次。现在将匹配项替换为自身,然后换行)。

如果您的文件名包含冒号或换行符,这些解决方案都无法正常工作。

答案3

您可以尝试使用awk而不是grep,例如仅打印基本名称:

find . -name '*.pb*' -exec awk 'function basename(file) {
sub(".*/", "", file)
return file
}
/pattern/{
print basename(FILENAME)":"FNR":"$0
}' {} +

并分割路径/行内容:

find . -name '*.pb*' -exec awk '/pattern/{print FILENAME"\n"FNR":"$0}' {} +

或者

find . -name '*.pb*' -exec awk '/pattern/{print FILENAME":"FNR":\n"$0}' {} +

如果您只想打印文件名一次,后跟该文件中的所有匹配行,并带有两个greps:

find . -name '*.pb*' -exec grep -l pattern {} \; -exec grep -n pattern {} \;

这些应该适用于所有类型的文件名(即使是包含冒号的文件名)...

答案4

使用grep -l

find . -type f  -exec grep -ilHn --color=always 'searchphrase' {} \;

相关内容