如何grep模式后的内容?
Posted
技术标签:
【中文标题】如何grep模式后的内容?【英文标题】:How to grep for contents after pattern? 【发布时间】:2012-05-08 16:03:18 【问题描述】:给定一个文件,例如:
potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789
我想对所有以potato:
开头的行进行 grep,但只对potato:
后面的数字进行管道传输。所以在上面的例子中,输出将是:
1234
5432
我该怎么做?
【问题讨论】:
【参考方案1】: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:
的行都被发送到一个内联(-e
)Perl 脚本,该脚本从stdin
中获取所有行,然后,对于这些行中的每一行,执行与第一个示例中相同的替换上面,然后打印出来。
或
awk 'if(/potato:/) print $2' < file.txt
文件通过stdin
发送(< file.txt
通过stdin
将文件的内容发送到左侧的命令)到awk
脚本,对于包含potato:
的每一行(@如果正则表达式 /potato:/
与当前行匹配,则 987654358@ 返回 true),打印第二个字段,如上所述。
或
perl -e 'for(<>)/potato:/ && s/^.*: // && print' < file.txt
文件通过stdin
(< file.txt
,见上文)发送到与上述类似的 Perl 脚本,但这次它还确保每一行都包含字符串 potato:
(/potato:/
是一个正则表达式,如果当前行包含 potato:
,则匹配,如果是 (&&
),则继续应用上述正则表达式并打印结果。
【讨论】:
不需要两个进程和一个管道。我会选择awk '$1 ~ /potato/ print $2 ' file.txt
。
awk 更习惯用法是awk '/potato:/ print $2'
Perl 脚本可以受益于perl -pe
【参考方案2】:
sed -n 's/^potato:[[:space:]]*//p' file.txt
可以将 Grep 视为受限 Sed,或将 Sed 视为广义 Grep。在这种情况下,Sed 是一款出色的轻量级工具,可以满足您的需求——当然,还有其他几种合理的方法可以做到这一点。
【讨论】:
【参考方案3】:或者使用正则表达式断言:grep -oP '(?<=potato: ).*' file.txt
【讨论】:
我从上面接受的答案中尝试了一些单行,但我觉得这个答案更准确地解决了这个问题。 一些解释:选项-o
表示只打印该行的匹配部分。而-P
推断出与 Perl 兼容的正则表达式,恰好是 positive lookbehind 正则表达式 (?<=string)
。
注意:由于-P
选项,此解决方案仅与GNU grep
兼容,不适用于您可以找到的POSIX grep
在 macOS 等环境中。【参考方案4】:
这将在每次匹配后打印所有内容,仅在同一行:
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代码
【讨论】:
【参考方案5】:您可以使用 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
要拆分的子字符串是“:\”,因为空格字符必须用反斜杠转义。
您可以在the linux documentation project 找到更多类似的内容。
【讨论】:
while read
非常慢;只要您选择一个带有缓冲 I/O 的工具(即几乎本答案中提到的任何工具,以及许多其他工具),使用外部工具实际上会快得多。
另外,你应该使用read -r
,除非你非常特别地需要POSIX之前的一些相当讨厌的遗留行为。【参考方案6】:
grep -Po 'potato:\s\K.*' file
-P
使用 Perl 正则表达式
-o
仅输出匹配项
\s
匹配potato:
后面的空格
\K
省略匹配
.*
匹配其余字符串
【讨论】:
感谢正则表达式的解释。 注意:由于-P
选项,此解决方案仅与GNU grep
兼容,不适用于您可以找到的POSIX grep
在 macOS 等环境中。【参考方案7】:
现代 BASH 支持正则表达式:
while read -r line; do
if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
echo "$BASH_REMATCH[1]"
fi
done
【讨论】:
【参考方案8】:grep potato file | grep -o "[0-9].*"
【讨论】:
以上是关于如何grep模式后的内容?的主要内容,如果未能解决你的问题,请参考以下文章