我正在尝试使用Kernel.txt在“Linux内核版本”或“USB_STATE = DISCONNECTED”的基础上重命名现有文件名。脚本正在运行,没有任何错误,但没有输出。更改的文件需要与之前的文件夹(F1,F2,F3)相同。
Top dir: Log
SubDir: F1,F2,F3
F1: .bin file,.txt file,.jpg file
F2: .bin file,.txt file,.jpg file
F3: .bin file,.txt file,.jpg file
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use File::Basename;
use File::Spec;
use Cwd;
chdir('C:\\doc\\logs');
my $dir_01 = getcwd;
my $all_file=find ({ 'wanted' => \&renamefile }, $dir_01);
sub renamefile
{
if ( -f and /.txt?/ )
{
my @files = $_;
foreach my $file (@files)
{
open (FILE,"<" ,$file) or die"Can not open the file";
my @lines = <FILE>;
close FILE;
for my $line ( @lines )
{
if($line=~ /Linux kernel Version/gi || $line=~ /USB_STATE=DISCONNECTED/gi)
{
my $dirname = dirname($file); # file's directory, so we rename only the file itself.
my $file_name = basename($file); # File name fore renaming.
my $new_file_name = $file_name;
$new_file_name =~ s/.* /Kernal.txt/g; # replace the name with Kernal.txt
rename($file, File::Spec->catfile($dirname, $new_file_name)) or die $!;
}
}
}
}
}
答案 0 :(得分:1)
此代码看起来有点像 cargo-cult programming 。也就是说,一些结构在这里没有迹象表明你正在理解它在做什么。
chdir('C:\\doc\\logs');
my $dir_01 = getcwd;
帮自己一个忙,并使用正斜杠,即使对于Windows路径名也是如此。这通常得到支持。
你的目录图表明有一个顶级目录Log
,但你还是C:/doc/logs
。它是什么?
您确实意识到$dir_01
是一个非描述性的名称,并且是您刚才想要的路径?此外,File::Find
不要求您在工作目录中启动。也就是说,chdir
在这里有点无用。你真的想要:
my $start_directory = "C:/doc/Log"; # or whatever
my $all_file=find ({ 'wanted' => \&renamefile }, $dir_01);
我不确定find
的返回值是什么意思。但我确信我们不必将它放入一些未使用的变量中。
当我们使用=>
fat逗号提供键名时,我们不必手动引用这些键。因此:
find({ wanted => \&renamefile }, $start_directory);
/.txt?/
此正则表达式执行以下操作:
tx
,t
。 ?
是一个零或一个量词。如果您想匹配以.txt
结尾的文件名,则应该
/\.txt$/
\.
与文字句点匹配。 $
将正则表达式锚定在字符串的末尾。
my @files = $_;
foreach my $file (@files) {
...;
}
这通常写成
my $file = $_;
...;
您将$_
的值分配给@files
数组,然后该数组具有一个元素:$_
内容。然后循环遍历此一个元素。这样的循环不值得称为循环。
open (FILE,"<" ,$file) or die"Can not open the file";
my @lines = <FILE>;
close FILE;
for my $line ( @lines )
{ ... }
啊,从哪里开始?
对文件句柄使用词法变量。这些具有关闭自己的好处。
对于错误处理,use autodie
。如果你真的想自己做,那么错误信息应该包含两个重要的信息:
$file
)$!
)这意味着像... or die "Can't open $file: $!"
。
不要将整个文件读入数组并循环遍历 。相反,使用类似while(<>)
的循环,可以节省内存并迭代。这一次只能读取一行,这要好得多。
组合,这看起来像
use autodie; # at the top
open my $fh, "<", $file;
LINE: while (<$fh>) {
...; # no $line variable, let's use $_ instead
}
哦,我标记了循环(带LINE
)供以后参考。
if($line=~ /Linux kernel Version/gi || $line=~ /USB_STATE=DISCONNECTED/gi) { ... }
在正则表达式上放置/g
标志会将它们变成迭代器。你真的不想那样。而且我不太确定这种不区分大小写的匹配是否真的是必要的。您可以使用正则表达式替换||
移动|
或正则表达式。由于我们现在使用$_
来包含行,因此我们不必手动将正则表达式绑定到字符串。因此,我们可以写:
if (/Linux Kernel Version|USB_STATE=DISCONNECTED/i) { ... }
my $dirname = dirname($file); # file's directory, so we rename only the file itself.
my $file_name = basename($file); # File name fore renaming.
默认情况下,原始$_
以及我们的$file
仅包含文件名,但不包含目录。这不是问题:File :: Find chdir
'd到正确的目录中。这使我们的处理更容易。如果您想拥有该目录,请使用$File::Find::dir
变量。
my $new_file_name = $file_name;
$new_file_name =~ s/.* /Kernal.txt/g;
/.* /
正则表达式说:
Kernal.txt
。 /g
标志在这里完全没用。您确定不希望Kernel.txt
使用 e 吗?为什么文件名中的空格?我不太明白。如果要将文件重命名为 Kernel.txt
,只需将其指定为字符串,而不是使用替换进行奇怪的操作:
my $new_file_name = "Kernel.txt";
rename($file, File::Spec->catfile($dirname, $new_file_name)) or die $!;
我们已经确定错误消息还应该包含文件名,甚至更好:我们应该使用自动错误处理。
此外,我们已经在正确的目录中,因此我们不必将新名称与目录连接。
rename $file => $new_file_name; # error handling by autodie
last LINE;
那应该够了。另请注意,我离开 LINE 循环。重命名文件后,也无需检查其他行。