模式后如何grep获取内容?


79

给定一个文件,例如:

potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789

我想grep开头的所有行,potato:但只管道后面的数字potato:。因此,在上面的示例中,输出为:

1234
5432

我怎样才能做到这一点?

Answers:


112
grep 'potato:' file.txt | sed 's/^.*: //'

grep查找包含字符串的任何行potato:,然后对于每行,从行()的开始到最后一次出现该序列(冒号后跟空格)的地方用空字符sed替换(s///-替换)任何字符(.*)字符串(-将第一部分替换为第二部分,该部分为空)。^:s/...//

要么

grep 'potato:' file.txt | cut -d\   -f2

对于包含的每一行potato:cut会将其分成多个由空格分隔的字段(-d\- d=定界符,\=转义的空格字符,类似的-d" "方法也可以使用),并打印每行的第二个字段(-f2)。

要么

grep 'potato:' file.txt | awk '{print $2}'

对于包含的每一行potato:awk将打印第二个字段(print $2),默认情况下用空格定界。

要么

grep 'potato:' file.txt | perl -e 'for(<>){s/^.*: //;print}'

包含的所有行都potato:被发送到inline(-ePerl脚本中,该脚本从中获取所有行stdin,然后对这些行中的每一个进行与上述第一个示例相同的替换,然后打印出来。

要么

awk '{if(/potato:/) print $2}' < file.txt

该文件通过stdin< file.txt将文件的内容通过stdin左命令发送)发送到awk脚本,该脚本针对包含的每一行potato:if(/potato:/)如果正则表达式/potato:/与当前行匹配,则返回true ),打印第二个字段,如所述以上。

要么

perl -e 'for(<>){/potato:/ && s/^.*: // && print}' < file.txt

该文件通过stdin< file.txt,请参见上文)发送到与上述脚本类似的Perl脚本,但是这一次还可以确保每行包含字符串potato:/potato:/是一个正则表达式,如果当前行包含potato:,则匹配,如果(&&),然后继续应用上述正则表达式并打印结果)。


3
不需要两个过程和一个管道。我会去的awk '$1 ~ /potato/ { print $2 }' file.txt
musiphil 2013年

2
awk会更惯用awk '/potato:/ {print $2}'
Benjamin W.

Perl脚本可能会受益于perl -pe
Tripleee

59

或使用正则表达式断言: grep -oP '(?<=potato: ).*' file.txt


4
我从上面接受的答案中尝试了一些方法,但是我觉得这个答案可以更准确地解决问题。
Jake88

3
一些解释:选项-o表示仅打印该行的匹配部分。而-P推断与Perl兼容的正则表达式,这恰好是则表达式后面的正向表达式(?<=string)
Serge Stroobandt

9
sed -n 's/^potato:[[:space:]]*//p' file.txt

可以将Grep视为受限的Sed,或将Sed视为广义Grep。在这种情况下,Sed是一种出色的轻量级工具,可以满足您的需求-当然,当然也存在其他几种合理的方法。


2

这将在每次比赛后打印所有内容,仅在同一行上:

perl -lne 'print $1 if /^potato:\s*(.*)/' file.txt

这将执行相同的操作,除了它还将打印所有后续行:

perl -lne 'if ($found){print} elsif (/^potato:\s*(.*)/){print $1; $found++}' file.txt

使用以下命令行选项:

  • -n 循环输入文件的每一行
  • -l 在处理之前删除换行符,然后再将它们重新添加
  • -e 执行Perl代码

2
grep -Po 'potato:\s\K.*' file

-P 使用Perl正则表达式

-o 仅输出匹配项

\s 匹配之后的空间 potato:

\K 省略比赛

.* 匹配其余的字符串


1

您可以使用grep作为其他答案。但是您不需要grep,awk,sed,perl,cut或任何外部工具。您可以使用纯bash进行操作。

试试看(分号可以让您将所有内容放在一行上):

$ while read line;
  do
    if [[ "${line%%:\ *}" == "potato" ]];
    then
      echo ${line##*:\ };
    fi;
  done< file.txt

##告诉bash从前面删除$ line中最长的匹配“:”。

$ while read line; do echo ${line##*:\ }; done< file.txt
1234
5678
5432
4567
5432
56789

或者,如果您想要键而不是值,%%告诉bash从末尾删除$ line中最长的匹配“:”。

$ while read line; do echo ${line%%:\ *}; done< file.txt
potato
apple
potato
grape
banana
sushi

要拆分的子字符串是“:\”,因为空格字符必须用反斜杠转义。

您可以在linux documentation项目中找到更多类似的东西。


while read非常慢;只要您选择一个带有缓冲I / O的外部实用程序(实际上是此答案中提到的任何一个,以及许多其他),实际上使用起来就会更快。
重奏

此外,read -r除非您非常明确地要求POSIX之前有一些令人讨厌的旧行为,否则您应该使用。
重奏

0

现代BASH支持正则表达式:

while read -r line; do
  if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
    echo "${BASH_REMATCH[1]}"
  fi
done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.