2013-07-04 14 views
5

Ich lerne Perl und schrieb ein kleines Perl-Skript-Dateien zu öffnen und entfernen Sie die Kommentare

# Will remove this comment

my $name = ""; # Will not remove this comment

#!/usr/bin/perl -w < - wird nicht entfernen Dieser spezielle Kommentar

Der Name der zu bearbeitenden Dateien wird als Argumente über das Terminal

übergeben
die "You need to a give atleast one file-name as an arguement\n" unless (@ARGV); 

foreach (@ARGV) { 
    $^I = ""; 
    (-w && open FILE, $_) || die "Oops: $!"; 
    /^\s*#[^!]/ || print while(<>); 
    close FILE; 
    print "Done! Please see file: $_\n"; 
} 

Nun, wenn ich lief es über Terminal: perl removeComments file1.pl file2.pl file3.pl

bekam ich die Ausgabe: Done! Please see file:

Dieses Skript arbeitet genau so, wie ich erwarte aber

Problem 1: Warum hat $_ den Namen der Datei nicht gedruckt?

Problem 2: Warum wird Done! Please see file: nur einmal gedruckt, da die Schleife 3 Mal ausgeführt wird?

Wie würden Sie dieses Skript in so wenigen Zeilen wie möglich schreiben?

Bitte kommentieren Sie auch meinen Code, wenn Sie Zeit haben.

Vielen Dank.

+0

Hinweis: '/ stuff /;' ist wirklich '$ _ = ~/stuff /;'. – cHao

+0

Es gibt wirklich keinen Grund, '<>' hier zu verwenden, wenn Sie explizit ein Datei-Handle geöffnet haben (was auch mit der 3-Arg-Methode gemacht werden sollte). – squiguy

+0

Der möglicherweise kürzeste Weg, um Ihr Programm zu schreiben: 'perl -i -ne '/^\ s * # [^!]/|| print'' außer den fertiggestellten Nachrichten. – TLP

Antwort

9

Die while speichert die vom Diamantoperator <> gelesenen Zeilen in $ _, also überschreibt man die Variable, die den Dateinamen speichert.

Auf der anderen Seite öffnen Sie die Datei mit open, aber verwenden Sie nicht den Griff zum Lesen; es verwendet stattdessen den leeren Diamantoperator. Der leere Diamantoperator macht eine implizite Schleife über Dateien in @ARGV und entfernt dabei Dateinamen, so dass die foreach nur einmal ausgeführt wird.

Um das zweite Problem zu beheben Sie while(<FILE>), oder schreiben Sie die Schleife verwenden, können das Programm in <> Vorteil der impliziten Schleife zu nehmen und schreiben als:

$^I = ""; 
/^\s*#[^!]/ || print while(<>); 
+0

Technisch ist es die while-Schleife, die das tut: 'while (<>)' ist eine spezielle magische Kurzschrift für 'while (definiert ($ _ = <>))'. In jedem anderen Kontext gibt '<>' jedoch nur die Zeile (n) zurück, die es liest. –

+0

Danke, dass du das geklärt hast, ich dachte wirklich das '<>' modifizierte '$ _'. – Joni

+1

Sie können 'while' für' for' vertauschen, was auch funktioniert, weil 'for' im Loop-Zustand Aliasing verwendet, ähnlich wie' for local $ _ (...) '. – TLP

2

Es ist nicht sicher mehrere Schleifen zu verwenden und versuchen, um das richtige zu bekommen $_. Der while Loop bringt Ihre $_ um. Versuchen Sie, Ihren Dateien innerhalb dieser Schleife bestimmte Namen zu geben. Sie können dies tun mit so:

foreach my $filename(@ARGV) { 
    $^I = ""; 
    (-w && open my $FILE,'<', $filename) || die "Oops: $!"; 
    /^\s*#[^!]/ || print while(<$FILE>); 
    close FILE; 
    print "Done! Please see file: $filename\n"; 
} 

oder so:

foreach (@ARGV) { 
    my $filename = $_; 
    $^I = ""; 
    (-w && open my $FILE,'<', $filename) || die "Oops: $!"; 
    /^\s*#[^!]/ || print while(<$FILE>); 
    close FILE; 
    print "Done! Please see file: $filename\n"; 
} 

Bitte verwenden Sie niemals Barewords für Dateihandies und tun open ein 3-Argument verwenden.

  • open my $FILE, '<', $filename - gute

  • open FILE $filename - schlecht

+0

Sie haben nicht bemerkt, dass '<>' nicht aus dem '$ FILE'-Dateihandle gelesen wird. – TLP

+0

ah genau! Vielen Dank –

3

Hier ist ein lesbarer Ansatz.

#!/usr/bin/perl 

# always!! 
use warnings; 
use strict; 

use autodie; 
use File::Copy; 

# die with some usage message 
die "usage: $0 [ files ]\n" if @ARGV < 1; 


for my $filename (@ARGV) { 
    # create tmp file name that we are going to write to 
    my $new_filename = "$filename\.new"; 

    # open $filename for reading and $new_filename for writing 
    open my $fh, "<", $filename; 
    open my $new_fh, ">", $new_filename; 

    # Iterate over each line in the original file: $filename, 
    # if our regex matches, we bail out. Otherwise we print the line to 
    # our temporary file. 
    while(my $line = <$fh>) { 
     next if $line =~ /^\s*#[^!]/; 
     print $new_fh $line; 
    } 

    close $fh; 
    close $new_fh; 

    # use File::Copy's move function to rename our files. 
    move($filename, "$filename\.bak"); 
    move($new_filename, $filename); 

    print "Done! Please see file: $filename\n"; 
} 

Beispielausgabe:

$ ./test.pl a.pl b.pl 
Done! Please see file: a.pl 
Done! Please see file: b.pl 

$ cat a.pl 
#!/usr/bin/perl 

print "I don't do much\n"; # comments dont' belong here anyways 

exit; 

print "errrrrr"; 

$ cat a.pl.bak 
#!/usr/bin/perl 

# this doesn't do much 
print "I don't do much\n"; # comments dont' belong here anyways 

exit; 

print "errrrrr"; 
0

Einfachere Lösung: Verwenden Sie $_ nicht.

Als Perl zum ersten Mal geschrieben wurde, wurde es als Ersatz für Awk und Shell konzipiert, und Perl entlehnte diese Syntax. Perl auch zur besseren Lesbarkeit erstellt die spezielle Variable $_ mit dem Sie verschiedene Befehle, ohne dass erstellen Variablen verwenden dürfen:

while (<INPUT>) { 
    next if /foo/; 
    print OUTPUT; 
} 

Das Problem ist, dass, wenn alles $_ verwendet, dann wird alles $_ in vielen effact unangenehme Nebenwirkungen.

Nun ist Perl eine viel anspruchsvoller Sprache und hat Dinge wie lokal Variablen scoped (Hinweis: Sie verwenden nicht local diese Variablen erstellen - das lediglich _package Variablen gibt (auch bekannt als globale Variablen) eine lokale Wert.)

Da Sie Perl lernen, können Sie auch Perl richtig lernen. Das Problem ist, dass es zu viele Bücher gibt, die immer noch auf Perl 3.x basieren. Finden Sie ein Buch oder web page, die moderne Praxis beinhaltet.

In Ihrem Programm wechselt $_ vom Dateinamen zur Zeile in der Datei und zurück zur nächsten Datei. Das verwirrt dich. Wenn Sie benannte Variablen verwendet haben, können Sie zwischen Dateien und Zeilen unterscheiden.

Ich habe Ihr Programm mit moderneren Syntax geschrieben, aber Ihre gleiche Logik:

use strict; 
use warnings; 
use autodie; 
use feature qw(say); 

if (not $ARGV[0]) { 
    die "You need to give at least one file name as an argument\n"; 
} 

for my $file (@ARGV) { 
    # Remove suffix and copy file over 
    if ($file =~ /\..+?$/) { 
     die qq(File "$file" doesn't have a suffix); 
    } 
    my ($output_file = $file) =~ s/\..+?$/./; #Remove suffix for output 
    open my $input_fh, "<", $file; 
    open my $output_fh, ">", $output_file; 
    while (my $line = <$input_fh>) { 
     print {$output_fh} $line unless /^\s*#[^!]/; 
    } 
    close $input_fh; 
    close $output_fh; 
} 

Das ist ein bisschen mehr Typisierung als Ihre Version des Programms, aber es ist einfacher zu sehen, was los ist und pflegen .

Verwandte Themen