在 AWK 命令中传递变量给 NR 不起作用

7

您好,我正在尝试使用awk命令在找到正则表达式后打印出5行。我有以下内容:

line_start=$(awk '/regex/{print NR}' file)
let line_end=$line_start+4
awk 'NR==$line_start, NR==$line_end' file

这段代码不会输出任何内容。它不会卡住,只会换到下一行。

我查阅了一些类似的问题,并看到其他人使用了-v选项。在这里我应该使用吗?他们的情况都是针对更大的awk脚本的。

顺便说一下,我正在使用Kornshell。

谢谢!

2个回答

16

你的脚本存在几个问题。其中一个即时的问题是在第二次调用 awk 时,你在脚本周围使用了单引号,所以 $line_start$line_end 不是由 shell 扩展的变量,而是作为脚本的一部分直接传递给 awk。你可以通过改用双引号来解决这个问题。

awk "NR==$line_start, NR==$line_end" file

这只能工作是因为$line_start$line_end是数字。如果它们是字符串,你就不能这样做,因为shell变量的值最终会被awk解析为awk代码的一部分,而不是字符串。通常情况下,要将字符串传递给awk脚本,可以使用带有-v的习语来定义与shell变量相同名称(或者如果你愿意,可以使用不同名称)的awk变量:
awk -v "line_start=$line_start" -v "line_end=$line_end" 'NR==line_start, NR==line_end' file

你的脚本存在更多问题。
- 你对文件进行了两次解析。如果文件很大,这可能会导致速度变慢;而且如果数据来自管道而不是磁盘文件,则无法实现。 - 如果/regex/有多个匹配项,那么$line_start将包含一个行号列表。Shell会在let行上报语法错误。
如果你想显示匹配项后的5行,请在awk内部进行计数。
awk '
  /regex/ { show_lines = 5 }
  show_lines { print; --show_lines; }
' file

如果您只想显示第一个匹配的块,请在 show_lines 达到 0 后退出。
  show_lines { print; --show_lines; if (!show_lines) exit; }

哇!你们太棒了,非常感谢!我没有所有这些信息可用。非常详细的答案,谢谢! - user1639103

2
您可以使用sed来完成此操作:
sed -n '/regex/{N;N;N;N;N;p}' file

或者改变awk的解决方案:

line_start=$(awk '/regex/{print NR}' file) 
let line_end=$line_start+4 
awk "{ if (NR>=$line_start && NR<=$line_end) print; }" file

另一种awk解决方案 (s.awk):
BEGIN           { v = -1} 
/regex/         { v = 0 } 
v > -1          { v++   }
v > -1 && v < 5 { print }
v == 5          { exit  }

使用:

awk -f s.awk file

在您发布的代码中,大多数情况下if是不必要的。只需将条件放在操作块之外即可。这就是OP尝试做的事情,也是Gilles在他的答案中所做的。 - Dennis Williamson
非常感谢大家!我现在理解了很多! - user1639103

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接