/ start /,/ end/range表达式在awk中是否有用?

Ed *_*ton 16 awk

我一直争辩说你永远不应该使用范围表达式,如:

/start/,/end/
Run Code Online (Sandbox Code Playgroud)

在awk中,因为虽然它只是简单的情况,你只想打印匹配的文本,包括起始和结束线比替代*更简洁:

/start/{f=1} f{print; if (/end/) f=0}
Run Code Online (Sandbox Code Playgroud)

当你想稍微调整它以做任何其他事情时,它需要完全重写或导致重复或其他不需要的代码.例如,如果您想使用上面的第二个表单打印除范围分隔符之外的匹配文本,您只需调整它以移动组件:

f{if (/end/) f=0; else print} /start/{f=1}
Run Code Online (Sandbox Code Playgroud)

但是如果你从一开始/start/,/end/就需要放弃那种方法来支持我刚发布的内容,或者你必须写下这样的内容:

/start/,/end/{ if (!/start|end/) print }
Run Code Online (Sandbox Code Playgroud)

即重复不希望的条件.

然后我看到一个问题,要求end在文件中识别LAST ,并在解决方案中使用范围表达式,我认为这似乎有一些价值(请参阅/sf/answers/1480150661/).

但现在,我又回想起根本不值得使用范围表达式,并且不使用范围表达式的解决方案也适用于该情况.

那么 - 有没有人有一个例子,其中范围表达式实际上为解决方案增加了显着的价值?

*我以前用过:

/start/{f=1} f; /end/{f=0}
Run Code Online (Sandbox Code Playgroud)

但是很多次我发现我必须做一些额外的事情,当它f是真的并且/end/被发现时(或者换一种方式只做一些事情,如果/end/发现IF f是真的那么)所以现在我只是试着坚持稍微不那么简短但更多强大且可扩展:

/start/{f=1} f{print; if (/end/) f=0}
Run Code Online (Sandbox Code Playgroud)

Scr*_*zer 12

有趣.我也经常从范围表达式开始,然后切换到使用变量..

我认为这种情况可能有用,除了纯粹的仅限范围的情况之外,如果你想打印一个匹配,但只有它在一定范围内.还因为它立即显而易见.例如:

awk '/start/,/end/{if(/ppp/)print}' file
Run Code Online (Sandbox Code Playgroud)

有了这个输入:

start
dfgd gd
ppp 1
gfdg
fd gfd
end
ppp 2 
ppp 3
start
ppp 4
ppp 5
end
ppp 6
ppp 7
gfdgdgd
Run Code Online (Sandbox Code Playgroud)

将产生:

ppp 1
ppp 4
ppp 5
Run Code Online (Sandbox Code Playgroud)

- 当然也可以使用:

awk '/start/{f=1} /ppp/ && f; /end/{f=0}' file
Run Code Online (Sandbox Code Playgroud)

但它更长,可读性更低..


Mic*_*ald 5

虽然您认为/start/,/end/范围表达式可以很容易地用条件重新实现,但它有许多有趣的用例,可以单独使用它。正如您所观察到的,它对于处理表格数据可能没有什么价值,这是awk的主要但不仅仅是用例。

那么 - 有没有人有一个例子,其中范围表达式实际上为解决方案增加了显着的价值?

在提到的用例中,范围表达式提高了易读性。下面是一些示例,其中范围表达式准确地选择了要处理的文本。这些只是一些示例,但类似的应用程序有无数,展示了awk令人难以置信的多功能性。

过滤某个时间范围内的日志

假设每个日志行都以 ISO 时间戳开头,下面的过滤器将选择给定 1 小时范围内的所有事件:

awk '/^2015-06-30T12:00:00Z/,/^2015-06-30T13:00:00Z/'
Run Code Online (Sandbox Code Playgroud)

从文件中提取文档

awk '/---- begin file.data ----/,/---- end file.data ----/'
Run Code Online (Sandbox Code Playgroud)

这可用于将资源与 shell 脚本(使用cat)捆绑在一起,提取部分 GPG 签名消息(使用 准备--clearsign)或更一般的 MIME 消息。

处理 LaTeX 文件

范围模式可用于匹配 LaTeX 环境,因此例如我们可以选择目录中所有文章的摘要:

awk '/begin{abstract}/,/end{abstract}/' *.tex
Run Code Online (Sandbox Code Playgroud)

或者所有的定理,准备一个定理数据库!

awk '/begin{theorem}/,/end{theorem}/' *.tex
Run Code Online (Sandbox Code Playgroud)

或者编写一个linter确保定理不包含引用(如果我们认为这是不好的风格):

awk '
  /begin{theorem}/,/end{theorem}/ { if(/\\cite{/) { c+= 1 } }
  END { printf("There were %d bad-style citations.\n", c) }
'
Run Code Online (Sandbox Code Playgroud)

或预处理表等。

  • 过滤日志文件的方法是有缺陷的,因为它要求这两个日期位于日志文件中。如果出于某种原因它们不是,则过滤将不起作用。 (5认同)
  • 关键是,如果您需要做任何稍微更有趣的事情,那么您需要完全重写或重复条件。例如,尝试增强 `awk '/begin{theorem}/,/end{theorem}/'` 以简单地不打印每个块的开始和行,您会发现需要复制内部的开始和结束条件操作部分并添加显式 print (`awk '/begin{theorem}/,/end{theorem}/{if (!(/begin{theorem}|end{theorem}/)) print}'`) 或您'需要重新设计它以使用标志 `awk '/end{theorem}/{f=0} f; /begin{theorem}/{f=1}'` 那么为什么不总是使用标志呢? (3认同)