2012-03-19 18 views
10

Si consideri il seguente script di sciocchezze come esempio:Prevenire perl di stampare i messaggi di avviso identici

use strict; 
use warnings; 

my $uninitialisedValue; 

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

cui viene eseguito dalla riga di comando:

$ perl warningPrinter.pl < longfile.txt 

Indipendentemente da ciò che STDIN contiene, lo STDOUT sarà essere pieno di:

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. 
... 

Io lavoro con file molto lunghi, quindi ricevere questo come output quando il mio copione è per lo meno lievemente irritante. Il processo può richiedere un po 'di tempo per rispondere a un segnale di terminazione CTRL-c e il mio terminale viene improvvisamente riempito con lo stesso messaggio di errore.

Esiste un modo per far stampare a Perl solo la prima istanza di un messaggio di avviso identico e ricorrente o semplicemente rendere i messaggi di avviso fatali per l'esecuzione dello script? Vedendo come non ho mai prodotto una sceneggiatura che funziona nonostante abbia avvertimenti in loro, accetterei entrambi. Ma probabilmente è più comodo se riesco a far perl stampare avvisi identici una sola volta.

+4

Prova questo altro post StackOverflow: [Come posso fare Perl morire se viene generato un avvertimento?] [1] [1]: http://stackoverflow.com/questions/3896060/how- can-i-make-perl-die-if-a-warning-è generato –

+1

Eccellente. 'use warnings FATAL => 'all'' funziona benissimo per uccidere il processo dopo un avviso nel mio caso. – MattLBeck

+1

Non utilizzare 'for' durante la lettura di un file, specialmente quando il file è grande, perché un ciclo for pre-carica il suo elenco in memoria. Usa 'while' invece. Inoltre, il reindirizzamento di un file su stdin è ridondante, basta usare il file come argomento. Nel tuo problema specifico, riaprire STDERR in un file può essere una soluzione. – TLP

risposta

10

Ho pensato di mostrarti come è possibile creare una logica di avviso univoca. Non consiglio, però:

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

dovrei dire che io non consiglio questo come un generale pratica . Perché è meglio avere una politica di avviso. È un'operazione che può assumere un valore indefinito o non si desidera gestire un valore undef. Provo a rimuovere tutti gli avvisi dal mio codice completato.

Nel primo caso, mettendo no warnings 'uninitialized'; nel ciclo for è un molto più facile - e regolare cosa da fare. Nel secondo caso, probabilmente vorrai fallire.

Tuttavia, se si tratta di qualcosa che in realtà si vorrebbe gestire ma si avverte una volta, si supponga di volere una gestione affidabile dei dati, ma si desidera avvisare i processi upstream che si ottengono alcuni dati errati, si potrebbe andare a creare un 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}++; 
    }; 
} 

E chiamare in questo modo:

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

Allora avete deciso qualcosa.

+0

Bello! Immagino che dovrei aggiungere questo tipo di cose a ogni script con cui voglio usare questa logica? Perché non lo consiglieresti? – MattLBeck

+0

@kikumbob Elaborerò nel mio post. – Axeman

+2

Si noti che il modulo 'diagnostics' fa tutto questo tranne per la parte riguardante il numero di riga del blocco di input. – tchrist

Problemi correlati