如何grep特定行_and_文件的第一行?

Modified on: Sat, 30 Jun 2018 10:16:30 +0800

假设一个简单的grep如:

$ psa aux | grep someApp
1000     11634 51.2  0.1  32824  9112 pts/1    SN+  13:24   7:49 someApp

这提供了很多信息,但由于缺少ps命令的第一行,因此没有信息的上下文。我更希望显示ps的第一行:

$ psa aux | someMagic someApp
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     11634 51.2  0.1  32824  9112 pts/1    SN+  13:24   7:49 someApp

当然,我可以为ps专门为grep添加一个正则表达式:

$ ps aux | grep -E "COMMAND|someApp"

但是,我更喜欢更通用的解决方案,因为在其他情况下我也想要第一行。

最佳答案

好方法

通常你不能用grep做这个,但是你可以使用其他工具。已经提到过AWK,但您也可以使用sed,如下所示:

sed -e '1p' -e '/youpattern/!d'

工作原理:

  1. Sed实用程序分别在每一行上运行,在每一行上运行指定的命令。您可以拥有多个命令,指定多个-e选项。我们可以在每个命令前面加上一个范围参数,该参数指定是否应该将此命令应用于特定行。

  2. “1p”是第一个命令。它使用p命令,通常打印所有行。但我们在它前面加上一个数值,指定它应该应用的范围。在这里,我们使用1,这意味着第一行。如果要打印更多行,可以使用x,yp,其中x是第一行打印,y是要打印的最后一行。例如,要打印前3行,您可以使用1,3p

  3. 下一个命令是d,它通常删除缓冲区中的所有行。在此命令之前,我们将yourpattern放在两个/字符之间。这是另一种方式(首先是指定我们使用p命令执行哪些行)的命令应该运行的寻址行。这意味着该命令仅适用于与yourpattern匹配的行。除此之外,我们在!命令之前使用d字符来反转其逻辑。所以现在它将删除所有匹配指定模式的行。

  4. 最后,sed会打印剩余在缓冲区中的所有行。但是我们删除了与缓冲区不匹配的行,因此只会打印匹配的行。

  5. 醇>

    总结一下:我们打印第一行,然后从输入中删除所有与我们的模式不匹配的行。其余的行被打印出来(因此只有匹配模式的行)。

    第一线问题

    正如评论中所提到的,这种方法存在问题。如果指定的模式也匹配第一行,它将被打印两次(一次是p命令,一次是因为匹配)。我们可以通过两种方式避免这种情况:

    1. 1d之后添加1p命令。正如我已经提到的,d命令从缓冲区删除行,我们用数字1指定它的范围,这意味着它只删除第一行。所以命令是sed -e '1p' -e '1d' -e '/youpattern/!d'

    2. 使用1b命令,而不是1p。这是一个技巧。 b命令允许我们跳转到标签指定的其他命令(这样可以省略一些命令)。但是如果没有指定这个标签(如我们的例子中那样),它只会跳到命令的末尾,忽略我们行的其余命令。所以在我们的例子中,最后的d命令不会从缓冲区中删除这一行。

    3. 醇>

      完整示例:

      ps aux | sed -e '1b' -e '/syslog/!d'
      

      使用分号

      一些sed实现可以通过使用分号分隔命令而不是使用多个-e选项来节省一些输入。因此,如果您不关心可移植性,那么命令将是ps aux | sed '1b;/syslog/!d'。它至少在GNU sedbusybox实现中起作用。

      疯狂的方式

      然而,这是使用grep执行此操作的相当疯狂的方法。这绝对不是最佳的,我发布这个仅仅是为了学习目的,但是你可以使用它,例如,如果你的系统中没有任何其他工具:
      ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog'
      

      如何运作

      1. 首先,我们使用-n选项在每行之前添加行号。我们想要计算我们匹配的所有行.* - 任何东西,甚至是空行。正如评论中所建议的那样,我们也可以匹配'^',结果是一样的。

      2. 然后我们使用扩展正则表达式,因此我们可以使用\|特殊字符作为OR。因此,如果行以1:(第一行)开头或包含我们的模式(在本例中为syslog),我们匹配。

      3. 醇>

        行号问题

        现在的问题是,我们在输出中得到了这个丑陋的行号。如果这是一个问题,我们可以使用cut删除它们,如下所示:

        ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-
        

        -d选项指定分隔符,-f指定我们要打印的字段(或列)。因此,我们希望在每个:字符上剪切每一行,并仅打印第二列和所有后续列。这有效地删除了第一列的分隔符,这正是我们所需要的。


相关问答

添加新评论