【问题标题】:Get the line number of the first line matching second pattern获取匹配第二个模式的第一行的行号
【发布时间】:2019-06-15 06:18:13
【问题描述】:

是否可以使用awk 或sed 获取一行的行号,使其成为匹配正则表达式的第一行,然后是另一行匹配另一个正则表达式的行?

换句话说:

  1. 查找匹配正则表达式 r1 的行 l1。 l1 是匹配 r1 的第一行。
  2. 找到线 l2 下面 l1。 l2 匹配正则表达式 r2。 l2 是匹配 r2 的第一行,忽略 l1 及以上的行。

澄清:匹配我的意思是部分匹配,对于大多数通用解决方案。 部分匹配当然可以变成与\<...\> 的全字匹配或与^...$ 的全行匹配。

示例输入:

- - '787928'
  - stuff
- - '810790'
  - more stuff
- - '787927'
  - yet more stuff
- - '828055'
  - some more stuff
- - '828472'
  - some other stuff

如果 r1 是 ^-.*787927.* 并且 r2 是 ^- 我希望输出为 7,即表示 - - '828055' 的行号。

【问题讨论】:

  • 抱歉,这不是 StackOverflow 的工作方式。 “我想做 X,请给我提示和/或示例代码”形式的问题被认为是题外话。请访问help center并阅读How to Ask,尤其是阅读Why is “Can someone help me?” not an actual question?
  • 不要使用pattern这个词,因为它非常模棱两可。而是使用单词regexp 或string,无论您是什么意思,并说明您是否需要部分、全字或全行匹配或其他内容。此外 - 无论您尝试做什么,都可以发布简洁、可测试的样本输入和完全满足您所有要求的预期输出。

标签: awk sed


【解决方案1】:

输入示例:

world
zekfzlefkzl
fezekzevnkzjnz
hello
zeniznejkglz
world
eznkflznfkel
hello
zenilzligeegz
world

命令:

pat1="hello"; pat2="world";
awk -v pat1=$pat1 -v pat2=$pat2 '$0 ~ pat1{pat1_match = 1}($0 ~ pat2)&&pat1_match{print NR; exit}' <input>

输出:

6

【讨论】:

  • 短一点:awk '/hello/,/world/ { if ($0 ~ /world/) {print NR; exit } }'
  • 谢谢!我不知道记录范围。对于像我这样想了解更多信息的人:gnu.org/software/gawk/manual/html_node/Ranges.html。很好的解决方案
  • 如果“world”在最后一行,这将失败。
  • 但引用Ed Morton:永远不要使用范围表达式(例如/start/,/end/),因为它们会使琐碎的任务变得非常简单,但随后需要重复条件或针对最小的需求更改进行完全重写.
  • 埃德莫顿确实是明智的。无论如何,感谢您向我展示了这个技巧。
【解决方案2】:

对于如下所示的输入文件:

 1  pat2
 2  x
 3  pat1
 4  x
 5  pat2
 6  x
 7  pat1
 8  x
 9  pat2

您可以按如下方式使用 sed:

$ sed -n '/pat1/,${/pat2/{=;q;};}' infile
5

它的工作原理是这样的:

sed -n '       # suppress output with -n
/pat1/,$ {     # for all lines from the first occurrence of "pat1" on...
    /pat2/ {   # if the line matches "pat2"
        =      # print line number
        q      # quit
    }
}' infile

如果pat1 的第一次出现与pat2 在同一行,则上述操作失败:

 1  pat2
 2  x
 3  pat1 pat2
 4  x
 5  pat2
 6  x
 7  pat1
 8  x
 9  pat2

将打印3。使用 GNU sed,我们可以改用它:

$ sed -n '0,/pat1/!{/pat2/{=;q;};}' infile
5
sed -n '     # suppress output
0,/pat1/! {  # for all lines after the first occurrence of "pat1"
    /pat2/ { # if the line matches "pat2"
        =    # print line number
        q    # quit
    }
}' infile

0 地址是 GNU 扩展;如果 pat1 在第一行,则使用 1 会中断。

【讨论】:

  • 我实际上尝试了一些非常相似的东西,但不知道用什么代替 {=;q;}。
  • 我刚刚对此进行了测试,但它实际上并没有完全正确的行为。如果pat2 可以匹配pat1 匹配的行,它将打印该行,而不是下一个匹配。请注意,在问题中我强调了 below 这个词。
  • @SU3 True - 一个可测试的输入和输出示例将有助于使这一点更加清晰。
  • 仍然是一个非常有用的答案。如果您想出处理异常情况的方法,请不要删除第一个解决方案。
  • @SU3 我已经为此添加了一个解决方案,但它需要 GNU sed。
【解决方案3】:

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

sed -n '/^-.*787927.*/{:a;n;/^-/!ba;=;q}' file

遇到以-.*787927.* 开头的行时,启动一个循环,将当前行替换为下一行,直到一行开始- 打印行号并退出。

【讨论】:

    猜你喜欢
    • 2017-07-16
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-27
    • 2013-08-13
    • 2014-12-08
    • 1970-01-01
    相关资源
    最近更新 更多