打印一行中模式的第 n 次出现(包括)

打印一行中模式的第 n 次出现(包括)

假设我有多行相似的到下面的文件中。

Turbo is a cat. cats are good. cats are not dog.
Coco is a black cat. cats are furry. cats are not dog.

现在,如果想要grep全部^.*cat但要特别提及捕获直到单词第一次(或第n次)出现cat
期望的输出:

Turbo is a cat
Coco is a black cat
*blah is a so and so cat*

我怎么可以grep呢?

PS:我很想grep仅使用(或其其他变体)得到答案。

PS:我不想 grep^.*cat.然后执行任何操作来删除 “。” 。我想要一个通用的答案。

答案1

POSIX grep,您只能选择打印整行,或者根本不打印该行内容。如果要转换该行,则需要使用其他工具,例如 sed 或 awk。要打印到第一次出现的位置cat

sed -n 's/cat.*/cat/'
awk 'sub(/cat.*/,"")'

打印最多出现的情况比较复杂。

sed -n 's/cat/&\
/3; T; P'
awk 'gsub(/cat/,"&\n") >= 3 {split($0, a, "\n"); printf "%s%s%s\n", a[1], a[2], a[3]}'

GNU grep,您可以使用该-o选项仅打印该行的匹配部分。使用-P选项激活 Perl 语法,以便非贪婪量词可用。

grep -P -o '^(.*?cat){1}'

将大括号中的数字替换为实际数字ncat要打印的最后一次出现的位置。

虽然可以使用扩展正则表达式 ( -E) 来表达相同的内容,但这需要一个复杂的正则表达式,其大小与要计数的部分的大小呈指数关系(cat此处)。

答案2

grep仅根据指定的正则表达式选择行并打印它们。

我认为您被迫通过管道传输输出行并使用附加命令来完成这项工作。

通常您可以使用sedawk来完成不使用 的工作grep,因为它们既可以选择行也可以替换字符串。

下面有一个解决方案,使用awk

awk -v word=cat -v n=2 'BEGIN {wordlength=length(word);} {line=$0;outputline="";position=index(line,word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,word);  } if (i!=1) {print outputline;}}'

您应该设置word要搜索的字符串以及n所需的出现次数。

考试:

$ awk -v word=cat -v n=2 'BEGIN {wordlength=length(word);} {line=$0;outputline="";position=index(line,word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,word);  } if (i!=1) {print outputline;}}' file
Turbo is a cat. cat
Coco is a black cat. cat

答案3

这是一个sed解决方案(例如,打印到并包括第二次出现的内容;替换2为您的编号。):

sed -n 's/cat/&\
/2
t print
d
:print
P' infile

这将禁用自动打印 via并尝试将第二次出现的+换行符-n替换。如果替换成功,它将分支到换行符并打印到换行符,否则该行将被删除。catcat:printPd


gnu sed可以将其写为一行(例如,打印到并包括第 5 次出现):

sed -n 's/cat/&\n/5;tt;d;:t;P' infile

相关内容