如何在Perl中实现Unix grep?

TCM*_*TCM 8 regex perl grep

如何grep在Perl中实现Unix?我试着使用Perl的内置功能grep.这是不起作用的代码:

$pattern = @ARGV[0];
$file= @ARGV[1];

open($fp,$file);

@arr = <$fp>;

@lines = grep $pattern, @arr;

close($fp);
print @lines;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我只尝试基本grep功能不全功能,其次我不想自己做字符串解析.我想使用内置grep或Perl的一些功能.

提前致谢 :)

Pab*_*cia 15

正如您已经接受了答案,我正在编写此答案以供将来读者搜索类似问题的参考,但不完全是您的:

正如人们已经回答的那样,使用perl模拟grep的方法是使用在线方法.对于使用perl作为'更好'的grep(以及查找和剪切......),我推荐本书最小的perl,你很幸运,因为'perl as a"grep'这一章是示例章节之一.

这里有更多来自本书的例子:

perl -wnle '/foo/ and print' null.txt  # normal grep
perl -wnle '/foo/ and print "$ARGV: $_"' null.txt # grep -H
perl -wnle '/foo/ and print $ARGV and close ARGV' null_1.txt null_2.txt # grep -l
Run Code Online (Sandbox Code Playgroud)

在最后一个示例中,ARGV是当前文件句柄,与-l一样,您有兴趣查找具有匹配项的文件,您可以打印文件名,并在文件中第一次匹配后转到下一个文件.

您也可以逐行搜索:

$ perl -00 -wnl -e '/\bBRIBE\b/i and print;' SenQ.testimony
I knew I'd be in trouble if
I ACCEPTED THE BRIBE!
So I did not.

My minimum bribe is $100k, and she only offered me $50k,
so to preserve my pricing power, I refused it.
Run Code Online (Sandbox Code Playgroud)

或者只找到第一场比赛:

$ perl -00 -wnl -e '/\bBRIBE\b/i and close ARGV;' SenQ.testimony
I knew I would be in trouble if
I ACCEPTED THE BRIBE!
So I did not.
Run Code Online (Sandbox Code Playgroud)

最后,如果你问grep和perl,我想我应该提一下ACK.它在perl中实现了grep功能并对其进行了扩展.这是一个很棒的工具,作为一个加分,你可以把它作为一个CPAN包.我总是使用命令行,我不知道你是否可以直接从你的perl程序访问它的方法,但这将是非常好的.


cod*_*ict 13

在Perl中引用我们使用的整个数组@.但是要引用我们使用的标量的各个元素$.

所以,你需要使用$而不是@在这些方面:

$pattern = @ARGV[0];
$file= @ARGV[1];
Run Code Online (Sandbox Code Playgroud)

这个

@lines = grep $pattern, @arr;
Run Code Online (Sandbox Code Playgroud)

应该

@lines = grep /$pattern/, @arr;
Run Code Online (Sandbox Code Playgroud)

Perl中的grep具有以下一般语法:

grep EXPR,LIST
Run Code Online (Sandbox Code Playgroud)

它评估EXPRfor的每个元素,LIST并返回由表达式求值为true的元素组成的列表值.

EXPR你的情况是搜索的模式$pattern阵列@arr.要搜索你需要使用/PATTERN/不带/字符串$pattern将被评估为true或false.

  • @coddadict,你做了很好的修复代码中的错误,但这个问题和用户使用的方法将其转换为准'xy问题',所以这是灌输可能不是主题的情况之一.我本来建议不要同时读取整个文件(他想模拟grep,这是面向行的),我会建议他使用grep {}而不是grep()只是为了创造一个好习惯,而三争论开放.甚至进一步向他展示在线方法(或者同时逐行排队)将是一个加分. (2认同)

Tot*_*oto 13

当然,codaddict的回答是正确的,但我想补充一些评论:

您应该始终使用以下两行开始脚本:

use strict;
use warnings;
Run Code Online (Sandbox Code Playgroud)

使用三个args打开并测试错误:

open my $fh, '<', $file or die "unable to open '$file' for reading : $!";
Run Code Online (Sandbox Code Playgroud)

因为use strict你必须声明所有变量.所以你的脚本将是这样的:

#!/usr/bin/perl

use strict;
use warnings;

my $pattern = $ARGV[0];
my $file = $ARGV[1];

open $fh, '<', $file or die "unable to open file '$file' for reading : $!";
my @arr = <$fh>;
close $fh;  # close as soon as possible

my @lines = grep /$pattern/, @arr;

print @lines;
Run Code Online (Sandbox Code Playgroud)

如果您的文件很大,您可以避免在内存中完全读取它:

#!/usr/bin/perl
use strict;
use warnings;

my $pattern = qr/$ARGV[0]/;
my $file= $ARGV[1];
print "pattern=$pattern\n";

my @lines;
open my $fh, '<', $file or die "unable to open file '$file' for reading : $!";
while(my $line=<$fh>) {
    push @lines, $line if ($line =~ $pattern);
}
close($fh);
print @lines;
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用'print $ line'替换while循环中的'push @lines,$ line',并避免使用任何数组.如果你的文件是"大"的,那么某人或某事将不可避免地有一天触发一个grep,几乎返回文件的所有行.(同样大:-) (2认同)

FMc*_*FMc 11

您可以grep直接在命令行上逼近原始版本.该-e选项允许您在命令行上定义Perl脚本.该-n选项大致包装你的脚本:while (<>){ SCRIPT }.

perl -ne 'print if /PATTERN/' FILE1 FILE2 ...
Run Code Online (Sandbox Code Playgroud)

grep在每个打印的匹配前面的文件名前缀稍微好一些.请注意,此示例与上面的示例一样,不会经历打开任何文件的麻烦.相反,我们使用Perl的<>构造来遍历所有文件,并且$ARGV变量提供当前文件名.

use strict;
use warnings;

my $pattern = shift;

while (my $line = <>){
    print $ARGV, ':', $line if $line =~ $pattern;
}
Run Code Online (Sandbox Code Playgroud)