【问题标题】:How to grep the last occurrence of a line pattern如何grep最后一次出现的线条图案
【发布时间】:2014-07-23 17:33:17
【问题描述】:

我有一个包含内容的文件

x
a
x
b
x
c

我想 grep 最后一次出现,

x
c

当我尝试时

sed -n  "/x/,/b/p" file

它列出了从xc 的所有行。

【问题讨论】:

  • 所以您想从最后一个x 打印到最后?
  • 据我所知 sed 分别操纵每一行。 (“你给它的命令依次在每一行输入上运行”——引用自en.flossmanuals.net/command-line/sed

标签: bash shell awk sed grep


【解决方案1】:

我不确定你的问题是否正确,所以这里有一些在黑暗中拍摄的照片:

  • 打印最后一次出现的x(正则表达式):

    grep x file | tail -1
    
  • 或者:

    tac file | grep -m1 x
    
  • 从第一个匹配行到结尾打印文件:

    awk '/x/{flag = 1}; flag' file
    
  • 从最后匹配的行到结尾打印文件(在不匹配的情况下打印所有行):

    tac file | awk '!flag; /x/{flag = 1};' | tac
    

【讨论】:

  • 这个awk "/x/ { flag = 1 }; { if (flag == 1) print; }" txt可以写得更简单一些像这样awk '/x/ {flag=1} flag' txt
  • 使用tac方便获取最后一个实例,真是别出心裁!谢谢
  • 在 awk 语句中使用 " 或 ' 时需要小心。我在否定时遇到了麻烦! - 我收到错误 "awk: cmd. line:1 ^backslash not last char - 因为我认为我必须使用 \!,但使用 ' 可以防止这种情况。
  • 另外 /x/ 是正则表达式,需要注意正则表达式中使用的字符,需要转义。幸运的是,我可以更改我必须搜索的文件中的字符串,这使得正则表达式更容易。
  • 注意:tac 需要 GNU 实用程序。
【解决方案2】:
grep -A 1 x file | tail -n 2

-A 1 告诉 grep 在匹配行之后打印一行
使用tail 可以得到最后两行。

或以相反的方式:

tac fail | grep -B 1 x -m1 | tac

注意:您应该确保您的模式足够“强大”,以便您获得正确的线条。即在开头用^ 括起来,在结尾用$ 括起来。

【讨论】:

    【解决方案3】:

    这可能对你有用(GNU sed):

    sed 'H;/x/h;$!d;x' file
    

    将最后一个x 和随后的内容保存在保留空间中,并在文件末尾打印出来。

    【讨论】:

      【解决方案4】:

      不确定如何使用sed,但您可以尝试awk

      awk '{a=a"\n"$0; if ($0 == "x"){ a=$0}}  END{print a}' file
      

      【讨论】:

        【解决方案5】:

        POSIX vi(或 ex 或 ed),以防它对某人有用

        当然是在命令模式下完成

        :set wrapscan

        转到第一行,然后向后搜索! 1G?pattern

        慢一点没有:set wrapscan

        G$?pattern

        解释:

        G转到最后一行

        移动到该行的末尾$

        ? 向后搜索pattern

        第一个向后匹配将与最后一个向前匹配相同

        无论哪种方式,您现在都可以删除当前(匹配)上方的所有行

        :1,.-1d

        kd1G

        如果同一行上有多个匹配项,您也可以使用d0 在删除行之前删除到匹配行的开头。

        POSIX awk,建议在 get last line from grep search on multiple files

        awk '(FNR==1)&&s{print s; s=""}/PATTERN/{s=$0}END{if(s) print s}'

        【讨论】:

          【解决方案6】:

          如果您想以真正可怕的单行方式执行 awk,但又想让 awk 更接近函数式编程范式语法,而不必跟踪最后一次出现的时间

          mawk/mawk2/gawk 'BEGIN { FS = "=7713[0-9]+="; RS = "^$";
          
           } END { print ar0[split($(0 * sub(/\n.+$/,"",$NF)), ar0, ORS)] }'
          

          这里我使用了多个 awk 简写:

              sub(/[\n.+$/, "", $NF) # trimming all extra rows after pattern
          

          g/sub() 返回替换的数量,因此将其乘以 0 会强制将 split() 拆分为完整文件 $0

          split() 返回数组中的项目数(这是表示最后一个元素位置的另一种方式),所以即使我已经修剪掉了尾随的\n,我仍然可以直接打印ar0[split()] ,知道 ORS 将填充缺少的尾随 \n

          这就是为什么这段代码看起来像是我试图在定义数组本身之前提取数组项,但由于需要逻辑流程,数组在它到达时被定义打印。

          现在,如果您想要更简单的东西,这两个也可以工作

          mawk/gawk 'BEGIN { FS="=7713[0-9]+="; RS = "^$" 
          
             } END { $NF = substr($NF, 1, index($NF, ORS)); 
                     
                     FS = ORS; $0 = $0; print $(NF-1) }'
          

          mawk/gawk '/=7713[0-9]+=/ { lst = $0 } END { print lst }'
          
          • 我没有使用与 OP 相同的 x|c 要求来展示这些工作,无论您需要固定字符串还是基于正则表达式的匹配。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-06-30
            • 1970-01-01
            • 2018-07-08
            • 2015-06-06
            • 1970-01-01
            • 2011-03-27
            • 1970-01-01
            相关资源
            最近更新 更多