【问题标题】:Is a /start/,/end/ range expression ever useful in awk?/start/,/end/ 范围表达式在 awk 中有用吗?
【发布时间】:2014-07-19 00:38:06
【问题描述】:

我一直认为你不应该使用这样的范围表达式:

/start/,/end/

在 awk 中,因为尽管它使您只想打印匹配文本(包括开始行和结束行)的情况比替代方案略短*:

/start/{f=1} f{print; if (/end/) f=0}

当您想要稍微调整它以执行其他任何操作时,它需要完全重写或导致重复或其他不受欢迎的代码。例如如果您想使用上面的第二种形式打印不包括范围分隔符的匹配文本,您只需调整它以移动组件:

f{if (/end/) f=0; else print} /start/{f=1}

但如果您从 /start/,/end/ 开始,则需要放弃这种方法,转而支持我刚刚发布的内容,否则您必须编写如下内容:

/start/,/end/{ if (!/start|end/) print }

即复制不需要的条件。

然后我看到一个问题发布,需要识别文件中的 LAST end 以及解决方案中使用范围表达式的位置,我认为这似乎有一些价值(请参阅 https://stackoverflow.com/a/21145009/1745001)。

不过,现在,我又开始思考,完全不值得为范围表达式烦恼,而不使用范围表达式的解决方案同样适用于这种情况。

那么 - 有没有人举例说明范围表达式实际上为解决方案增加了显着的价值?

*我曾经使用过:

/start/{f=1} f; /end/{f=0}

但太多次我发现当f 为真并且/end/ 被发现时我必须做一些额外的事情(或者换句话说,只有在/end/ 被发现时才做某事如果f 是真的)所以现在我只是尝试坚持稍微不那么简短但更健壮和可扩展的:

/start/{f=1} f{print; if (/end/) f=0}

【问题讨论】:

  • 我最近才了解范围表达式,我喜欢它们!当然,他们没有什么是“标志”变量无法做到的,但我认为它们有用的。诚然,在使用f 时,您正在减少(一种)重复,但在这样做时,您有责任在记录之间跟踪f。这实际上意味着为了理解脚本,您必须(至少)阅读两次,而不是一次。
  • @TomFenech 您将如何处理增强脚本以不打印开始/结束行?扔掉原来的并从一个变量重新开始,或者在操作块或其他东西中使用if 引入开始/结束条件的重复?我对范围表达式的担忧是,如果/当您的要求发生变化时,恕我直言,没有合理的方法可以在此基础上进行构建。
  • 我认为if 没有任何问题。这是两个正则表达式的简单组合,无论如何,这两种方法都不能很好地适应多种条件。我猜你可以/start/ {getline; do { print; getline } while (!/end/)}如果你真的想要;)
  • if 的问题是您正在复制代码,因此如果您稍后必须测试不同的条件,那么您需要在 2 个地方进行相同的更改,这通常是不可取的在软件中。 wrt getline 建议 - 这充满了问题,不应实施,如果您正在考虑使用 getline,请确保您阅读并完全理解 awk.info/?tip/getline
  • 我只是在开玩笑说使用getline,但无论如何感谢链接:) 在避免重复方面,您始终可以将模式设置为变量并使用~ 运算符:$0~s, $0~e {if(!($0~s||$0~e)) print}文件`。无论如何,在我看来,所有的方法都是 hacky,所以每个人都有自己的想法。

标签: awk


【解决方案1】:

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

我认为除了纯范围的情况之外,这可能有用的情况是,如果您想打印匹配项,但前提是它位于某个范围内。也因为它的作用很明显。例如:

awk '/start/,/end/{if(/ppp/)print}' file

使用此输入:

start
dfgd gd
ppp 1
gfdg
fd gfd
end
ppp 2 
ppp 3
start
ppp 4
ppp 5
end
ppp 6
ppp 7
gfdgdgd

将产生:

ppp 1
ppp 4
ppp 5

-- 当然也可以使用:

awk '/start/{f=1} /ppp/ && f; /end/{f=0}' file

但它更长,可读性稍差..

【讨论】:

  • 好的,我会考虑的,谢谢你的回复。默认情况下,我会使用/start/{f=1} f{if (/ppp/) print; if (/end/) f=0},因为这是对/start/{f=1} f{print; if (/end/) f=0} 的基本解决方案的明显增强。
  • +1:我还发现我们可以做一些简单的事情,例如通过说 awk '/patt/,0' file 而不是 awk '/patt/{p=1}p' file 来从模式打印到文件末尾
  • 我将此答案标记为已接受,因为我认为归根结底这没什么大不了的,如果有时人们更喜欢使用范围表达式作为起点,至少 if/when需求发生了变化,以至于不再有意义,它立即发生,因此他们没有那么多代码需要重写。这也意味着您可以编写一个看起来像等效 sed 解决方案的 awk 解决方案,因此它可能会帮助人们不要试图增强 sed 解决方案来做一些复杂的事情。感谢大家的回复。
  • @EdMorton。谢谢你,谢谢你的讨论,我觉得很有趣。您建议的标准方法/start/{f=1} f{print; if (/end/) f=0} 完美模仿/start/,/end/,而其他方法可能更像是一种近似。因此,我认为如果您希望以后能够在不重写的情况下对其进行扩展,那么这是一个很好的代码......
【解决方案2】:

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

那么 - 有没有人举例说明范围表达式实际上为解决方案增加了显着的价值?

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

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

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

awk '/^2015-06-30T12:00:00Z/,/^2015-06-30T13:00:00Z/'

从文件中提取文档

awk '/---- begin file.data ----/,/---- end file.data ----/'

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

处理 LaTeX 文件

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

awk '/begin{abstract}/,/end{abstract}/' *.tex

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

awk '/begin{theorem}/,/end{theorem}/' *.tex

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

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

或预处理表,

【讨论】:

  • 重点是,如果您需要做任何更有趣的事情,那么您需要完全重写或复制条件。例如,尝试增强 awk '/begin{theorem}/,/end{theorem}/' 以简单地不打印每个块的开始和行,您会发现您需要在操作部分中复制开始和结束条件并添加显式打印 (awk '/begin{theorem}/,/end{theorem}/{if (!(/begin{theorem}|end{theorem}/)) print}') 或者您需要重新设计它以使用标志awk '/end{theorem}/{f=0} f; /begin{theorem}/{f=1}' 那么为什么不总是使用标志呢?
  • 过滤日志文件的方法有缺陷,因为它要求这两个日期在日志文件中。如果由于某种原因它们不是,则过滤将不起作用。
猜你喜欢
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 2014-08-01
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多