我正在学习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:
只打印一次?
如何在尽可能少的行中编写此脚本?
如果你有时间,也请评论我的代码。
谢谢。
答案 0 :(得分:9)
while存储由菱形运算符&lt;&gt;读取的行。进入$ _,所以你要写的是存储文件名的变量。
另一方面,您使用open
打开文件,但实际上并未使用句柄来阅读;它使用空钻石运算符代替。空菱形运算符对@ARGV
中的文件进行隐式循环,删除文件名,因此foreach
只运行一次。
要解决第二个问题,您可以使用while(<FILE>)
,或者重写循环以利用<>
中的隐式循环并将整个程序编写为:
$^I = "";
/^\s*#[^!]/ || print while(<>);
答案 1 :(得分: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";
答案 2 :(得分: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参数open
。
open my $FILE, '<', $filename
- 好
open FILE $filename
- 糟糕
答案 3 :(得分:0)
更简单的解决方案:不要使用$_
。
首次编写Perl时,它被认为是Awk和shell的替代品,而Perl则大量借用了这种语法。 Perl 的可读性创建了特殊变量$_
,它允许您使用各种命令而无需创建变量:
while ( <INPUT> ) {
next if /foo/;
print OUTPUT;
}
问题是如果一切都在使用$_
,那么一切都会在许多不愉快的副作用中产生$_
。
现在,Perl是一种更复杂的语言,并且具有本地作用域变量之类的东西(提示:你不使用local
来创建这些变量 - 这只是给出了_package变量(又名全局变量)是一个本地值。)
由于您正在学习Perl,因此您可能正确学习Perl 。问题是有太多的书仍然基于Perl 3.x.找一本包含现代实践的书或web page。
在您的程序中,$_
从文件名切换到文件中的行,然后返回到下一个文件。这让你感到困惑。如果使用了命名变量,则可以区分文件和行。
我使用更现代的语法重写了你的程序,但你的逻辑是相同的:
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;
}
这比你的程序版本更加打字,但是更容易看到正在发生的事情和维护。