awk 或 sed 命令在特定行匹配正则表达式,如果成功则退出 true,否则 false

awk 或 sed 命令在特定行匹配正则表达式,如果成功则退出 true,否则 false

我需要确定文件是否包含某一行的某些正则表达式如果找到则返回 true(退出 0),否则返回 false。也许我想得太多了,但事实证明我的尝试有点笨拙。我有一个解决方案,但我正在寻找其他我没有想到的解决方案。我可以使用 perl,但我希望尽可能保持这种“轻量级”,因为它在木偶执行周期中运行。

这个问题很常见:​​在 RHEL6 中,screen 的打包方式将终端宽度限制为 80 个字符,除非您取消注释 132 处的行。此命令检查该行是否已修复:

 awk 'NR==132 && /^#termcapinfo[[:space:]]*xterm Z0=/ {x=1;nextfile} END {exit 1-x}' /etc/screenrc

注意:如果文件少于 132 行,则必须以 false 退出。

我认为sed这里会有帮助,但显然你必须做一些奇怪的技巧,比如空替换和分支。尽管如此,我还是想看到一个 sed 解决方案来学习。也许还有其他我忽略的事情。

编辑1:添加nextfile到我的 awk 解决方案中

编辑 2:基准 编辑 3:不同的主机(空闲)。编辑 4:错误地使用 Gile 的 awk 时间来优化每个运行。编辑5:新长凳

基准测试

首先,注意:wc -l /etc/screenrc216。当线路不存在时,50k 次迭代,以挂壁时间测量:

  • 空操作:0.545s
  • 我原来的 awk 解决方案:58.417
  • 我编辑的 awk 解决方案(使用 nextfile):58.364s
  • 贾尔斯的 awk 解决方案:57.578s
  • 优化的 perl 解决方案 90.352s 哎哟!
  • Sed 132{p;q}|grep -q ...解:61.259s
  • Cuonglm 的tail | head | grep -q:70.418s 哎哟!
  • 唐_克里斯蒂的head -nX |head -n1|grep -q:116.9s 呜呜呜!
  • Terdon 的双 grep 解决方案:65.127s
  • John1024的sed解决方案:45.764秒

谢谢约翰,谢谢 sed!老实说,我很惊讶 Perl 在这里能达到同等水平。 Perl 在启动时加载一堆共享库,但只要操作系统缓存所有这些库,它就取决于解析器和字节编码器。在遥远的过去(perl 5.2?)我发现它慢了 20%。正如我最初预期的那样,Perl 速度较慢,但​​由于我的复制/粘贴错误,Perl 似乎更好。

基准测试第 2 部分

最大的有实用价值的配置文件是/etc/services.因此,我为此文件重新运行了这些工作台,并且要更改的行是文件中的 2/3。总行数为 1100,因此我选择了 7220 并相应地修改了正则表达式(以便在一种情况下失败,在另一种情况下成功;对于工作台,它总是失败)。

  • 约翰的 sed 解决方案:121.4s
  • 克里斯蒂的{head;head}|grep解:138.341s
  • 康格姆的tail|head|grep解决方案:77.948秒
  • 我的 awk 解决方案:175.5s

答案1

使用 GNU sed:

sed -n '132 {/^#termcapinfo[[:space:]]*xterm Z0=/q}; $q1'

怎么运行的

  • 132 {/^#termcapinfo[[:space:]]*xterm Z0=/q}

    在第 132 行,检查 regex ^#termcapinfo[[:space:]]*xterm Z0=。如果找到退出,q则默认退出代码为 0。文件的其余部分将被跳过。

  • $q1

    如果到达最后一行 ,$则退出并退出代码 1: q1

效率

由于不需要读取文件的第 132 行,因此一旦到达第 132 行或文件末尾(以先发生者为准),此版本就会退出:

sed -n '132 {/^#termcapinfo[[:space:]]*xterm Z0=/q; q1}; $q1'

处理空文件

对于空文件,上面的版本将返回 true。这是因为,如果文件为空,则不会执行任何命令,并且 sed 会以默认退出代码 0 退出。要避免这种情况:

! sed -n '132 {/^#termcapinfo[[:space:]]*xterm Z0=/q1; q}'

这里,sed 命令以代码 0 退出,除非找到所需的字符串,在这种情况下,它以代码 1 退出。前面!告诉 shell 反转此代码以返回到我们想要的代码。!所有 POSIX shell 都支持该修饰符。该版本甚至适用于空文件。 (帽子提示:G-Man)

答案2

使用 POSIX 工具箱:

tail -n +132 </etc/screenrc | head -n 1 | grep -q pattern

答案3

您可以在 awk 中更有效地完成此操作:一旦到达相关行就退出。

awk 'NR==132 {if (/^#termcapinfo[[:space:]]*xterm Z0=/) found=1; exit}
     END {exit !found}' /etc/screenrc

或者,您可以使用 GNU sed(但便携式 sed 不允许您指定退出代码)。

或者,您可以使用将工具组合在一起的 Unix 哲学:使用head和提取所需的行tail,并将其传递给grep

</etc/screenrc tail -n +132 | head -n 1 |
grep -q '^#termcapinfo[[:space:]]*xterm Z0='

或者您可以使用 sed 提取所需的行:

</etc/screenrc sed -n '32 {p; q;}' |
grep -q '^#termcapinfo[[:space:]]*xterm Z0='

(这两者都依赖于这样一个事实:您希望空行和太短的文件获得相同的结果。)

对于如此小的文件,最快的方法可能是使用单个工具,因为启动多个程序的开销将大于使用专用工具(例如headtail和)所获得的性能增益sed。如果您想要第 132000000 行,从 开始tail -n +132000000可能比其他任何东西都快。

答案4

我知道你说过你不想使用perl.我认为您对它的“轻量级”有误解。

你可以这样做:

#!/usr/bin/env perl

use strict;
use warnings;

open ( my $input_fh, '<', "/etc/screenrc" ) or die $!; 
while ( <$input_fh> ) {
   if ( $. == 132 
   and m/^#termcapinfo[[:space:]]*xterm Z0=/ ) {
       exit 0; 
   }
}

exit 1;

您可以将其压缩为一个衬垫:

perl -ne 'exit 0 if $. == 132 and  m/^#termcapinfo[[:space:]]*xterm Z0=/ END { exit 1 }' 

相关内容