2012-03-19 7 views
10

Rozważmy następujący scenariusz nonsense jako przykład:Prevent perla od drukowania identyczne komunikaty ostrzegawcze

use strict; 
use warnings; 

my $uninitialisedValue; 

while(<>){ 
    print ${$uninitialisedValue}{$_},"\n"; 
} 

Która jest uruchamiany z linii poleceń:

$ perl warningPrinter.pl < longfile.txt 

Niezależnie od tego, co zawiera stdin, stdout będzie być pełen:

Use of uninitialized value in print at warningPrinter.pl line 16, <> line 1. 

Use of uninitialized value in print at warningPrinter.pl line 16, <> line 2. 

Use of uninitialized value in print at warningPrinter.pl line 16, <> line 3. 

Use of uninitialized value in print at warningPrinter.pl line 16, <> line 4. 
... 

Pracuję z bardzo długimi plikami, więc odbieram to jako wyjście, gdy tes mój scenariusz jest co najmniej łagodnie irytujący. Proces może zareagować na sygnał zakończenia CTRL-c, a mój terminal jest nagle wypełniony tym samym komunikatem o błędzie.

Czy istnieje sposób na uzyskanie perla, aby wydrukować tylko pierwsze wystąpienie identycznego i powtarzającego się komunikatu ostrzegawczego, lub po prostu spowodować, że komunikaty ostrzegawcze będą śmiertelne w wykonaniu skryptu? Widząc, że nigdy nie napisałem scenariusza, który działa pomimo ostrzeżeń w nich, akceptuję albo. Ale jest to prawdopodobnie wygodniejsze, jeśli uda mi się przekonać perla do drukowania identycznych ostrzeżeń tylko raz.

+4

Spróbuj innego StackOverflow wpis: [Jak mogę dokonać Perl umrze, jeśli generowane jest ostrzeżenie?] [1] [1]: http://stackoverflow.com/questions/3896060/how- can-i-make-perl-die-if-a-warning-is-generowany –

+1

Doskonały. 'użyj ostrzeżeń FATAL => 'all'' działa świetnie dla zabicia procesu po ostrzeżeniu w moim przypadku. – MattLBeck

+1

Nie używaj 'for' podczas czytania pliku, szczególnie gdy plik jest duży, ponieważ pętla for wstępnie ładuje swoją listę do pamięci. Zamiast tego użyj 'while'. Przekierowanie pliku na standardowe wejście jest zbyteczne, wystarczy użyć pliku jako argumentu. W twoim konkretnym problemie ponowne otwarcie pliku STDERR do pliku może być rozwiązaniem. – TLP

Odpowiedz

10

Pomyślałem, że pokażę ci, jak można stworzyć unikalną logikę ostrzegawczą. Nie polecam go jednak:

my %printed; 
local $SIG{__WARN__} = sub { 
    my $message = shift; 
    my ($msg, $loc) = $message =~ m/(.*?) at (.*?line \d+)/; 
    print $message unless $printed{$loc}{$msg}++; 
}; 

powinienem powiedzieć, że nie polecam tego jako ogólnym praktyce. Ponieważ lepiej jest mieć politykę ostrzegania. Jest to operacja, która może przyjąć niezdefiniowaną wartość lub nie chcesz obsłużyć wartości undef. Próbuję usunąć wszystkie ostrzeżenia z mojego wypełnionego kodu.

W pierwszym przypadku, stawiając no warnings 'uninitialized'; w pętli for jest znacznie łatwiejsze - i regularne rzeczą do zrobienia. W drugim przypadku prawdopodobnie zechcesz.

Jeśli jednak jest to coś, z czym chciałbyś sobie poradzić, ale ostrzegasz, powiedz, że potrzebujesz solidnej obsługi danych, ale chciałeś ostrzec procesy, że masz złe dane, możesz utworzyć sub warn_once:

{ use Carp(); 
    my %warned; 
    sub warn_once { 
     my $message = shift; 
     my ($msg, $loc) = $message =~ m/(.*?) at (.*?line \d+)/; 
     Carp::carp($message) unless $warned{$loc}{$msg}++; 
    }; 
} 

i nazwać tak:

while (<>) { 
    warn_once('$uninitialisedValue is uninitialized') 
     unless defined($uninitialisedValue) 
     ; 
    no warnings 'uninitialized'; 
    print ${$uninitialisedValue}{$_},"\n"; 
} 

Wtedy postanowiliśmy coś.

+0

Nice! Sądzę, że musiałbym dodać takie rzeczy do każdego skryptu, z którego chcę korzystać z tej logiki? Dlaczego nie poleciłbyś tego? – MattLBeck

+0

@kikumbob Opiszę w moim poście. – Axeman

+2

Należy zauważyć, że moduł 'diagnostics' wykonuje to wszystko z wyjątkiem części dotyczącej uwzględnienia numeru linii porcji wejściowej. – tchrist