列出特定行不包含特定字符串的文件

列出特定行不包含特定字符串的文件

我正在尝试查找所有具有扩展名的文件.md

find . -type f -name "*.md"

然后我想过滤掉那些第二行(行号= 2)不包含以开头和结尾的确切字符串的文件author: Mr. Xab Ycd

我该如何做第二部分?这样做grep效率很低,因为它会检查整个文件。

答案1

find . -type f -name '*.md' -exec \
  sh -c 'sed 1d\;q "$1" | grep -qvx "author: Mr. Xab Ycd"' sh {} \; -print

上述命令包含所有要求,而无需通过 shell 管道传递可能令人困惑的文件名。

第一部分是按原样(几乎)从您的复制的 - 查找名为*.md.我已将双引号更改为“硬”单引号;您的情况没有功能差异,但如果您想查找名为*.$mdthen 双引号的文件,则会尝试扩展变量$md.

然后匹配的文件名通过另一个测试通过-exec。 exec 的参数是一个小的 shell 脚本,其作用是确定 .txt 中给定文件名的成功或失败$1。该sed命令仅打印第二行;有不同的方法可以做到这一点,例如:

  • sed -n '2{p;q;}'或者
  • sed '1d;q

第一个说“默认情况下不打印行,但是当您看到第二行时,打印它然后退出”。第二个表示“默认打印行,但删除第一行,然后退出(在第二行);该q命令将在退出之前打印当前缓冲区。

该行文本(如果有)被传递给 grep,后者检查整行是否与给定文本匹配(或不匹配)。如果是的话不是match ( -v),则整个命令成功并find打印文件名。

答案2

find . -type f -name '*.md' -exec awk '
    FNR == 2 && $0 == "author: Mr. Xab Ycd" { exit 1 }
    FNR >  2 { exit 0 }' {} ';' -print

这将用于awk过滤掉至少两行长且第二行正是您提到的字符串的任何文件。如果第二行 ( FNR == 2) 与字符串完全相同,它会通过显式退出并以非零退出状态来实现此目的。如果到达第二行之后的任何行,它也会以零退出状态退出,以免解析不必要的内容。

如果以零退出状态退出(在第二行未找到该字符串),该find命令将继续打印文件的路径名。-printawk

答案3

zsh

by_Xab() {
  local line
  {
    IFS= read -r line &&
      IFS= read -r line &&
      [[ $line = "author: Mr. Xab Ycd" ]]
  } < ${1-$REPLY}
}
printf '%s\n' **/*.md(D.^+by_Xab)

每个文件最多读取 2 行,并且不执行任何命令(都是内置命令),因此比find -exec每个文件运行一个或多个命令的方法要高效得多。

使用 GNU awk,你可以这样做:

STRING='author: Mr. Xab Ycd' find . -name '*.md' -type f -exec gawk '
  BEGINFILE {found = 0}
  FNR == 2  {found = $0 == ENVIRON["STRING"]; nextfile}
  ENDFILE   {if (!found) print FILENAME}' {} +

它将运行一次find调用,并使用尽可能-exec ... {} +少的gawk调用语法。

答案4

find一次提供一个文件并且打印的责任在于find

find . -type f -exec perl -lne '$. == 2 && exit +/^author: Mr\. Xab Ycd$/' {} \; -print

这里find提供一堆文件,打印任务由以下命令处理perl

find . -type f -size 0 -print -o -exec perl -lne '
   print $ARGV if $. == 2 && !/^author: Mr\. Xab Ycd$/;
   close(ARGV),next if $. == 2;
   print($ARGV),close(ARGV) if eof;
' {} +

关闭-关闭ARGV是必不可少的 OTW 线路计数器,又名,$。未针对即将到来的文件进行初始化。

请注意,需要 eof 子句,对于长度 > 1 的文件,永远不会到达第二行,并且永远不会检查第二行。

相关内容