我需要确定文件是否包含某一行的某些正则表达式如果找到则返回 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/screenrc
是216
。当线路不存在时,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='
(这两者都依赖于这样一个事实:您希望空行和太短的文件获得相同的结果。)
对于如此小的文件,最快的方法可能是使用单个工具,因为启动多个程序的开销将大于使用专用工具(例如head
、tail
和)所获得的性能增益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 }'