2016-12-05 53 views
0

我正在寫一個腳本來解析Java中的線程轉儲。由於某些原因,當我嘗試從子例程內或在嵌套循環內讀取時,它根本不會進入嵌套循環。理想情況下,我希望能夠在嵌套循環上對STDIN進行操作,否則您將不得不編寫一些醜陋的狀態轉換代碼。如何在perl中執行<STDIN>的嵌套讀取?

在我使用STDIN之前,爲了確保我的子程序沒有指向STDIN的獨立指針,我將它打開爲$in

當我運行它時,它看起來像下面。儘管外層循環有更多來自STDIN的文件可以讀取,但您可以看到它永遠不會進入嵌套循環。

~/$ cat catalina.out-20160* | thread.dump.find.all.pl 
in is GLOB(0x7f8d440054e8) 
found start of thread dump at 2016-06-17 13:38:23 saving to tdump.2016.06.17.13.38.23.txt 
in is GLOB(0x7f8d440054e8) 
BEFORE NESTED STDIN 
BUG!!!! 
found start of thread dump at 2016-06-17 13:43:05 saving to tdump.2016.06.17.13.43.05.txt 
in is GLOB(0x7f8d440054e8) 
BEFORE NESTED STDIN 
BUG!!!! 
... 

代碼:

#!/usr/bin/perl 
use strict; 
use warnings; 
use Getopt::Long; 
use DateTime::Format::Strptime; 
use DateTime::Format::Duration; 
use Data::Dumper; 
# DO NOT touch ARGV! 
Getopt::Long::Configure("pass_through"); 

# cat catalina.out-* | thread.dump.find.all.pl 



sub processThreadDump { 
    my $in=$_[0]; 
    my $currentLine=$_[1]; 
    my $prevLine=$_[2]; 
    my $parsedDatetime=$_[2]; 

    # 2016-09-28 09:27:34 
    $parsedDatetime=~ s/[ \-\:]/./g; 
    my $outfile="tdump.$parsedDatetime.txt"; 
    print " saving to $outfile\n"; 
    print " in is $in\n"; 
    open(my $out, '>', $outfile); 
    print $out "$prevLine\n"; 
    print $out "$currentLine\n"; 
    print "BEFORE NESTED STDIN\n"; 
    foreach my $line (<$in>) { 
     print "INSIDE NESTED STDIN\n"; 
     $line =~ s/\R//g; #remove newlines 
     print $out "$line\n"; 
     if($line =~ m/JNI global references:/) { 
      print "PROPERLY LEFT NESTED STDIN\n"; 
      close($out); 
      return; 
     } elsif($line =~ m/Found \d+ deadlock\./) { 
      print "PROPERLY LEFT NESTED STDIN\n"; 
      close($out); 
      return; 
     } 
    } 
    print "BUG!!!!\n"; 
    close($out); 
} 

open(my $in, '<-'); 
print "in is $in\n"; 
my $prevLine; 
# read from standard in 
foreach my $line (<$in>) { 
    $line =~ s/\R//g; #remove newlines 
    if($line =~ m/Full thread dump OpenJDK 64-Bit Server VM/) { 
     # we found the start of a thread dump 
     print "found start of thread dump at ${prevLine}"; 
     processThreadDump($in, $line, $prevLine); 
    } else { 
     #print "setting prev line to $line\n"; 
     $prevLine=$line; 
    } 
} 
close($in); 
+1

最好在一個地方讀取任何流。不要嵌套它們。相反,請爲輸入設置一個狀態機。然後可以根據狀態將每行輸入分派到適當的處理。 – shawnhcorey

+0

@shawnhcorey:是的,工作。但是,我發現功能比狀態機更容易推理。 – joseph

+0

時間來升級你的技能。 – shawnhcorey

回答

0

當你說foreach my $line (<$in>),這使perl的開始循環之前讀取整個$in文件句柄。你可能想更重要的是這樣的:

while (defined(my $line = <$in>)) 

這隻會一次讀取一行,丟棄它,你與它完成。

+0

這是我認爲它很懶惰的壞處。 – joseph

+1

@Tanktalus ***不!它不是。嘗試'perl -MO = Deparse -e'while(<>){print「no \ n」}''......另外,你不會在其他答案中犯同樣的錯誤(例如[this酮](https://stackoverflow.com/a/40748273/100754))。 –

4

foreach遍歷一個列表,所以<>是在列表環境,因此它讀取一切從文件句柄。所以當你將$in傳遞給sub時,就沒有輸入了。請參閱I/O Operators in perlop

您可以一次讀取一行while (my $line = <$in>),但我不確定這是否會影響算法的其餘部分。

或者,如果您確實提前閱讀了所有輸入內容,爲什麼不僅僅使用一行數組呢?

+0

zdim,這個答案也適用於我,但Tanktalus排在第一位。抱歉。 :( – joseph

+0

@joseph這絕對沒問題 - 謝謝你這麼說,但你永遠不必解釋那個決定:)。而且,他們確實是第一個:)無論如何 - 如果先讀取所有輸入文件,然後再考慮先執行該文件,然後再處理,而不是再使用文件句柄。 – zdim