要搜索的字串是:
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);
}
}
uj5u.com熱心網友回復:
此代碼無法編譯,它包含導致其無法執行的錯誤。您永遠不應該發布您沒有首先嘗試運行的代碼。
問題的根源在于,對于多行匹配,您無法以逐行模式讀取檔案,您必須將整個檔案放入變數中。但是,您的程式包含許多缺陷。我將演示。以下是您的代碼摘錄(帶有固定縮進和缺少花括號)。
首先,始終使用:
use strict;
use warnings;
這將為您省去很多麻煩和長時間搜索隱藏的問題。
system ("rm -rf $dir/log.txt");
這在 Perl 中做得更好,您可以在其中控制錯誤:
unlink "$dir/log.txt" or die "Cannot delete '$dir/log.txt': $!";
foreach my $list (@files) {
# ^^
在回圈本身中宣告回圈變數,而不是在它之前。
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
,因為我們 slurp 檔案,所以我們沒有回圈。
此外,您通常應該選擇合適的變數名稱。我認為,如果您有檔案串列,則不應呼叫包含檔案名的變數$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
uj5u.com熱心網友回復:
以下示例代碼演示了正則運算式logger($fname,$msg)
在子例程的多行情況下的用法。
代碼片段假設輸入檔案相對較小并且可以讀入變數$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;
}
參考:正則運算式修改,取消鏈接,perlvar
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/506247.html