在模式之间返回多行的高效非贪婪方法

Ele*_*eck 4 bash awk sed

我有这样一个文件:

bar 1
 foo 1
  how now
  manchu 50
 foo 2
  brown cow
  manchu 55
 foo 3
  the quick brown
  manchu 1
bar 2
 foo 1
  fox jumped
  manchu 8
 foo 2
  over the
  manchu 20
 foo 3
  lazy dog
  manchu 100
 foo 4
  manchu 5
 foo 5
  manchu 7
bar 3
bar 4
Run Code Online (Sandbox Code Playgroud)

我想搜索'manchu 55'并收到:

FOONUMBER = 2

(上面'manchu 55'的foo#)

BARNUMBER = 1

(上面的那个酒吧#foo)

PHRASETEXT ="棕色牛"

('满洲55'上方的文字)

所以我最终可以输出:

棕色的牛,酒吧1,foo 2.

到目前为止,我已经完成了一些非常丑陋的grep代码,如:

FOONUMBER=`grep -e "manchu 55" -e ^" foo" -e ^"bar" | grep -B 1 "manchu 55" | grep "foo" | awk '{print $2}'`

BARNUMBER=`grep -e ^" foo $FOONUMBER" -e ^"bar" | grep -B 1 "foo $FOONUMBER" | grep "bar" | awk '{print $2}'`

PHRASETEXT=`grep -B 1 "manchu 55" | grep -v "manchu 55"`
Run Code Online (Sandbox Code Playgroud)

这段代码有3个问题:

  • 这让我感到畏缩,因为我知道这很糟糕
  • 它很慢; 我必须经历成千上万的条目,这需要太长时间
  • 有时,如我的例子中的第2栏,foo 4和5,'manchu'上面没有文字.在这种情况下,它错误地返回一个foo,这不是我想要的.

我怀疑我可以用sed这样做,做类似的事情:

FOONUMBER=`sed -n '/foo/,/manchu 55/p' | grep foo | awk '{print $2}'
Run Code Online (Sandbox Code Playgroud)

不幸的是,sed太贪心了.我一直在阅读AWK和状态机,这似乎是一个更好的方法来做到这一点,但我仍然不能很好地理解它设置它.

正如你现在可能已经确定的那样,编程不是我为生活所做的,但最终我已经把这个推向了我.我希望能够重写我已经拥有的更高效,并且希望不会太复杂,因为没有编程学位的其他一些可怜的草皮可能最终将不得不支持在未来某个日期对它进行的任何更改.

gle*_*man 6

用awk:

awk -v nManchu=55 -v OFS=", " '
  $1 == "bar" {bar = $0}    # store the most recently seen "bar" line
  $1 == "foo" {foo = $0}    # store the most recently seen "foo" line 
  $1 == "manchu" && $2 == nManchu {print prev, bar, foo} 
  {prev = $0}               # remember the previous line
' file
Run Code Online (Sandbox Code Playgroud)

输出

  brown cow, bar 1,  foo 2
Run Code Online (Sandbox Code Playgroud)

使用"nManchu = 100"输出运行

  lazy dog, bar 2,  foo 3
Run Code Online (Sandbox Code Playgroud)

这样做的好处是只需要通过文件一次,而不是解析文件3次,得到"bar","foo"和prev行.

  • Upvoted,只是一个侧面案例:manchu 5(将打印`foo 4,bar 2,foo 4`):我会在打印行条件中添加`&& prev!〜/(bar | foo)/` (2认同)