5

我正在学习 Perl 并编写了一个小脚本来打开 perl 文件并删除注释

# Will remove this comment

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

#!/usr/bin/perl -w<- 不会删除此特殊评论

要编辑的文件的名称通过终端作为参数传递

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";
}

现在当我通过终端运行它时: perl removeComments file1.pl file2.pl file3.pl

我得到了输出: Done! Please see file:

这个脚本完全按照我的预期工作,但是

问题1:为什么$_不打印文件名?

问题 2:既然循环运行了 3 次,为什么Done! Please see file:只打印了一次?

您将如何用尽可能少的行编写此脚本?

如果您有时间,也请评论我的代码。

谢谢你。

4

4 回答 4

9

The while stores the lines read by the diamond operator <> into $_, so you're writing over the variable that stores the file name.

On the other hand, you open the file with open but don't actually use the handle to read; it uses the empty diamond operator instead. The empty diamond operator makes an implicit loop over files in @ARGV, removing file names as it goes, so the foreach runs only once.

To fix the second issue you could use while(<FILE>), or rewrite the loop to take advantage of the implicit loop in <> and write the entire program as:

$^I = "";
/^\s*#[^!]/ || print while(<>);
于 2013-07-04T06:40:52.247 回答
3

这是一种更具可读性的方法。

#!/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";
}

样本输出:

$ ./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";
于 2013-07-04T07:22:30.740 回答
2

使用多个循环并尝试获得正确的$_. while循环正在杀死你的$_. 尝试在该循环中为您的文件指定特定名称。你可以这样做:

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";
}

或者这样:

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";
}

请不要对文件句柄使用裸词,并使用 3-argument open

  • open my $FILE, '<', $filename- 好的

  • open FILE $filename- 坏的

于 2013-07-04T06:43:05.377 回答
0

更简单的解决方案:不要使用$_.

当 Perl 第一次被编写时,它被认为是 Awk 和 shell 的替代品,Perl 大量借鉴了这种语法。Perl 还为可读性创建了特殊变量$_,它允许您使用各种命令而无需创建变量:

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

问题是,如果一切都在使用$_,那么一切都会产生$_许多令人不快的副作用。

现在,Perl 是一种更复杂的语言,并且具有局部作用域变量之类的东西(提示:您不使用local创建这些变量——它只是给 _package 变量(也称为全局变量)一个局部值。)

既然你在学习 Perl,那你还不如正确地学习 Perl。问题是仍然基于 Perl 3.x 的书籍太多了。查找包含现代实践的书籍或网页。

在您的程序中,$_从文件名切换到文件中的行并返回到下一个文件。这就是让你感到困惑的地方。如果使用命名变量,则可以区分文件和行。

我已经使用更现代的语法重写了您的程序,但您的逻辑相同:

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;
}

这比您的程序版本需要更多的输入,但更容易查看和维护正在发生的事情。

于 2013-07-04T11:36:15.240 回答