如何像grep -B这样做只选择一行?

时间:2014-08-06 22:28:04

标签: linux bash shell scripting

一切都在标题中。基本上让我说我有这种模式

some text lalala
another line 
much funny wow grep

我很有趣,我希望我的输出是“lalala”

谢谢

3 个答案:

答案 0 :(得分:2)

一个可能的答案是使用edex来执行此操作(其中很简单):

ed - yourfile <<< 'g/funny/.-2p'

(或者将ed替换为ex。您可能还有red,受限制的编辑器;它无法修改文件。)这会查找模式{{1} } global,并且每当找到它时,在匹配行(即/funny/部分)之前打印第2行。或者,如果你想在匹配'funny'的行之前包含'lalala'的最新行:

.-2p

唯一的问题是,如果您尝试处理标准输入而不是文件;然后你必须将标准输入保存到文件并处理该文件,这会破坏并发性。

你不能在ed - yourfile <<< 'g/funny/?lalala?p' 中做负偏移(虽然GNU sed允许你做正偏移,所以你可以使用sed让'lalala'变成'有趣'基于找到'lalala'的行(这不是你想要的),但你找不到基于找到'有趣'的'lalala'行。标准sed -n '/lalala/,+2p' file根本不允许偏移。

如果您只需要打印在模式匹配行之前的8行上找到的IP地址,则需要一个稍微复杂一点的sed脚本,但它仍然可行:

ed

这使用相同的基本机制来查找正确的行,然后运行替换命令以删除行上最后一个空格的所有内容并打印修改后的版本。由于没有ed - yourfile <<< 'g/funny/.-8s/.* //p' 命令,因此它实际上不会修改文件。

答案 1 :(得分:1)

由于grep -B仅在匹配之前打印每个完整行数,因此您必须将输出通过管道输入grep或Awk。

grep -B 2 "funny" file|awk 'NR==1{print $NF; exit}'

您也可以使用Awk。

awk -v s="funny" '/[[:space:]]lalala$/{n=NR+2; o=$NF}NR==n && $0~s{print o}' file

如评论中所述,匹配前8行IP地址的具体示例:

awk -v s="funny" '
/[[:space:]][0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ {
    n=NR+8
    ip=$NF
}
NR==n && $0~s {
    print ip
}' file

这些Awk解决方案首先找到您可能需要的输出字段,然后仅在您想要的单词存在于n后面的行中时打印输出。

答案 2 :(得分:0)

这是尝试略微概括的Awk解决方案。它维护最后q行的循环队列,并在看到匹配时在队列的头部打印该行。

#!/bin/sh
: ${q=8}
e=$1
shift
awk -v q="$q" -v e="$e" '{ m[(NR%q)+1] = $0 }
    $0 ~ e { print m[((NR+1)%q)+1] }' "${@--}"

适应不同的默认值(我将其设置为8)或正确的选项处理(目前,您将像q=3 ./qgrep regex file一样运行)以及记住(并因此打印)整行应该是很容易。

(如果你在第一行q - 1行看到一个匹配项,我也没有理由让它正常工作。那么它只会打印一个空行。)