perl 从目录中多个文件中搜索多行字符串

qv7cva1a  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(170)

要搜索的字符串为:

the file_is being created_automaically {
  period=20ns     }

我使用的Perl脚本如下(这个脚本对于单行字符串工作正常,但对于多行字符串不工作)

#!/usr/bin/perl
my $dir = "/home/vikas";
my @files = glob( $dir . '/*' );
#print "@files";
system ("rm -rf $dir/log.txt");

my $list;
foreach $list(@files){
if( !open(LOGFILE, "$list")){
open (File, ">>", "$dir/log.txt");
select (File);
print " $list \: unable to open file"; 
close (File);

else {   
while (<LOGFILE>){
if($_ =~ /".*the.*automaically.*\{\n.*period\=20ns.*\}"/){ 
open (File, ">>", "$dir/log.txt");
select (File);

print " $list \: File contain the required string\n";  
close (File);

     break;
}
}   
close (LOGFILE);
}
}
uurity8g

uurity8g1#

此代码未编译,它包含导致其无法执行错误您不应发布未首先尝试运行代码
问题的根源在于,对于多行匹配,你不能以逐行模式读取文件,你必须将整个文件放入一个变量中。然而,你的程序包含许多缺陷。我将演示。以下是你的代码摘录(带有固定的缩进和缺失的大括号)。
首先,始终用途:

use strict;
use warnings;

这将为您节省许多麻烦和长时间搜索隐藏的问题。

system ("rm -rf $dir/log.txt");

这在Perl中更好,在Perl中可以控制错误:
第一次
在循环本身中声明循环变量,而不是在它之前。

if( !open(LOGFILE, "$list")){
    open (File, ">>", "$dir/log.txt");
    select (File);
    print " $list \: unable to open file"; 
    close (File);

在打印到文件句柄之前,您不必显式地select文件句柄。您只需打印到文件句柄:print File "...."。您所做的只是更改STDOUT文件句柄,这不是一件好事。
另外,这是错误日志,应该转到STDERR。这可以通过在程序开始时打开STDERR文件来完成。为什么要这样做?如果你不是在终端调试程序,例如通过Web或其他进程,STDERR不会显示在屏幕上。否则,这只是调试时的额外工作。

open STDERR, ">", "$dir/log.txt" or die "Cannot open 'log.txt' for overwrite: $!";

这样做的另一个好处是不必先删除日志。现在,您可以这样做:

if (! open LOGFILE, $list ) {
    warn "Unable to open file '$list': $!";
} else ....

warn将转到STDERR,因此它基本上与print STDERR相同。
说到open,你应该使用三个参数open和显式的文件句柄,所以它变成:

if (! open my $fh, "<", $list )
} else {
    while (<LOGFILE>) {

因为你要查找多行匹配,你需要替换为slurp文件。这可以通过将输入记录分隔符设置为undef来完成。通常如下所示:

my $file = do { local $/; <$fh> };    # $fh is our file handle, formerly LOGFILE

接下来如何应用正则表达式:

if($_ =~ /".*the.*automaically.*\{\n.*period\=20ns.*\}"/) {

$_ =~是可选的。如果没有使用其他变量,正则表达式将自动与$_匹配。
你可能不应该在正则表达式中使用"。除非你在目标字符串中有"。我不知道你为什么把它放在那里,也许你认为字符串需要在正则表达式中用引号括起来。如果你这样做了,那就错了。为了匹配上面的字符串,你需要:

if( /the.*automaically.*{.*period=20ns.*}/s ) {

您不必对\大括号{}或等号=进行转义。您不必使用引号。/s修饰符使.(通配符句点)也匹配换行符,因此我们可以删除\n。我们可以从字符串开头或结尾删除.*,因为这是隐含的,正则表达式匹配始终是部分匹配,除非使用了锚点。

break;

关键字break只用于switch特性,这是实验性的,而且你没有使用它,或者启用它。所以它只是一个简单的词,这是错误的。如果你想提前退出循环,你可以使用last。注意,我们不必使用last,因为我们对文件进行了sloop,所以我们没有循环。
另外,你通常应该选择合适的变量名。如果你有一个文件列表,我认为包含文件名的变量不应该被称为$list。它被称为$file是合乎逻辑的。输入文件句柄不应该被称为LOGFILE,它应该被称为$input,或者$infh(输入文件句柄)。
这是我得到的,如果我把上面的应用到你的程序:

use strict;
use warnings;

my $dir     = "/home/vikas";
my @files   = glob( $dir . '/*' );
my $logfile     = "$dir/log.txt";

open STDERR, ">", $logfile or die "Cannot open '$logfile' for overwrite: $!";

foreach my $file (@files) {
    if(! open my $input, "<", $file) {
        warn "Unable to open '$file': $!"; 
    } else {
        my $txt = do { local $/; <$fh> };
        if($txt =~ /the.*automaically.*{.*period=20ns.*}/) {
            print " $file  : File contain the required string\n";
        }
    }
}

请注意,print将转到STDOUT,而不是错误日志。将STDOUT和STDERR放到同一个文件中并不常见。如果需要,可以在shell中重定向输出,如下所示:

$ perl foo.pl > output.txt
uqxowvwt

uqxowvwt2#

下面的示例代码演示了如何使用logger($fname,$msg)子例程将regex用于多行情况。
代码片段假设输入文件相对较小,可以读入变量$data(假设计算机有足够的内存读入)。

注意:输入数据文件应与主目录$ENV{HOME}中的其他文件区分开来,在此代码示例中,假定这些文件与模式test_*.dat匹配,也许您不打算 * 扫描 * 主目录中的所有文件(可能有数千个文件,但您只对少数几个感兴趣)

#!/usr/bin/env perl

use strict;
use warnings;
use feature 'say';

my($dir,$re,$logfile);

$dir = '/home/vikas/';
$re  = qr/the file_is being created_automaically \{\s+period=20ns\s+\}/;

$logfile = $dir . 'logfile.txt';

unlink $logfile if -e $logfile;

for ( glob($dir . "test_*.dat") ) {
    if( open my $fh, '<', $_ ) {
        my $data = do { local $/; <$fh> };
        close $fh;
        logger($logfile, "INFO: $_ contains the required string")
            if $data =~ /$re/gsm;
    } else {
        logger($logfile, "WARN: unable to open $_");
    }
}

exit 0;

sub logger {
    my $fname = shift;
    my $text  = shift;

    open my $fh, '>>', $fname
        or die "Couldn't to open $fname";
        
    say $fh $text;
    
    close $fh;
}

引用:正则表达式修改,unlinkperlvar

相关问题