好办法
通常,您无法使用grep执行此操作,但可以使用其他工具。已经提到了AWK,但是您也可以使用sed
,例如:
sed -e '1p' -e '/youpattern/!d'
这个怎么运作:
sed实用程序分别在每一行上工作,并在每行上运行指定的命令。您可以有多个命令,并指定了多个-e
选项。我们可以在每个命令之前添加一个range参数,该参数指定是否应将此命令应用于特定行。
“ 1p”是第一个命令。它使用p
通常会打印所有行的命令。但是我们在其前面添加了一个数值,该数值指定了应应用的范围。在这里,我们使用的1
是第一行。如果要打印更多行,则可以使用x,yp
where x
是要打印的第一行,y
是要打印的最后一行。例如,要打印前三行,您将使用1,3p
下d
一条命令通常从缓冲区中删除所有行。在此命令之前,我们将yourpattern
两个/
字符放在中间。这是另一种方法(应该首先使用p
命令指定行)来寻址命令应在其上运行的行。这意味着该命令仅适用于匹配的行yourpattern
。除此以外,我们!
在d
命令之前使用字符来反转其逻辑。因此,现在它将删除所有与指定模式不匹配的行。
最后,sed将打印缓冲区中剩余的所有行。但是我们从缓冲区中删除了不匹配的行,因此仅会打印匹配的行。
总结一下:我们打印第一行,然后从输入中删除所有与模式不匹配的行。行的其余部分被印刷(即所以只有线做匹配图案)。
一线问题
如评论中所述,这种方法存在问题。如果指定的图案也与第一行匹配,它将被打印两次(通过p
命令一次,并且由于匹配而一次)。我们可以通过两种方式避免这种情况:
在1d
之后添加命令1p
。正如我已经提到的,d
命令从缓冲区中删除行,并且我们以数字1指定它的范围,这意味着它只会删除第一行。所以命令是sed -e '1p' -e '1d' -e '/youpattern/!d'
使用1b
命令代替1p
。这是一个把戏。b
命令允许我们跳转到标签指定的其他命令(这样可以省略某些命令)。但是,如果未指定此标签(如我们的示例),它将仅跳转到命令的末尾,而忽略该行的其余命令。因此,在本例中,最后一条d
命令不会从缓冲区中删除此行。
完整示例:
ps aux | sed -e '1b' -e '/syslog/!d'
使用分号
某些sed
实现可以使用分号来分隔命令而不是使用多个-e
选项,从而节省了一些键入操作。因此,如果您不关心可移植性,则命令为ps aux | sed '1b;/syslog/!d'
。它至少在GNU sed
和busybox
实现中有效。
疯狂的方式
但是,这是使用grep执行此操作的相当疯狂的方法。绝对不是最佳选择,我发布此信息只是出于学习目的,但是如果您的系统中没有任何其他工具,则可以使用它,例如:
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog'
这个怎么运作
首先,我们使用-n
选项在每行之前添加行号。我们想对我们匹配的所有行进行编号.*
-任何东西,甚至是空行。如注释中所建议,我们也可以匹配“ ^”,结果是相同的。
然后,我们使用扩展的正则表达式,因此我们可以使用\|
用作OR的特殊字符。因此,如果行以1:
(第一行)开头或包含我们的模式(在本例中为syslog
),我们就匹配。
行号问题
现在的问题是,我们在输出中得到了这个丑陋的行号。如果出现问题,可以使用删除它们cut
,如下所示:
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-
-d
选项指定定界符,-f
指定我们要打印的字段(或列)。因此,我们希望剪切每个:
字符上的每一行并仅打印第二行和所有后续列。这将有效地删除带有分隔符的第一列,而这正是我们所需要的。
ack
是如此有用,为什么perl
飙升过去sed
,awk
等受欢迎:它的部分总结成一个连贯的整体是非常重要的。