0

我试图在“Linux 内核版本”或“USB_STATE=DISCONNECTED”的基础上用 Kernel.txt 重命名现有文件名。脚本正在运行,没有任何错误,但没有输出。更改后的文件需要与之前位于同一文件夹中(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 $!; 
        }
     }
   }
  } 
 }
4

1 回答 1

1

这段代码看起来有点像货物狂热的编程。也就是说,有些构造在这里没有表明你正在理解它在做什么。


chdir('C:\\doc\\logs');
my $dir_01 = getcwd;

帮自己一个忙并使用正斜杠,即使对于 Windows 路径名也是如此。这通常是支持的。

您的目录图说有一个顶级目录Log,但您 chdir 到C:/doc/logs. 它是什么?

您确实意识到这$dir_01是一个非常不具描述性的名称,并且是您刚刚 chdir'd 到的路径吗?此外,File::Find不需要您在工作目录中启动。也就是说,chdir这里有点没用。你实际上想要:

my $start_directory = "C:/doc/Log"; # or whatever

my $all_file=find ({ 'wanted' => \&renamefile }, $dir_01);

我不确定 的返回值是什么find意思。但我确信我们不必将它放入一些未使用的变量中。

当我们用粗逗号提供键名时=>,我们不必手动引用这些键。所以:

find({ wanted => \&renamefile }, $start_directory);

/.txt?/

此正则表达式执行以下操作:

  • 匹配任何字符(不是换行符),
  • 其次是文字tx
  • 和可选的t. the?是一个零或一的量词。

如果你想匹配以 结尾的文件名.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 ) 
{ ... }

啊,从哪里开始?

  1. 对文件句柄使用词法变量。这些具有关闭自己的好特性。

  2. 对于错误处理,use autodie. 如果你真的想自己做,错误信息应该包含两个重要的信息:

    • 您无法打开的文件的名称 ( $file)
    • 打开失败的原因($!

    那将意味着类似... or die "Can't open $file: $!".

  3. 不要将整个文件读入一个数组并循环遍历。相反,要节省内存并使用 -like 循环遍历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标志放在正则表达式上会将它们变成迭代器。你真的不想要那个。而且我不太确定这种不区分大小写的匹配是否真的有必要。您可以||使用 regex alternation 将 or 移动到正则表达式中|。正如我们现在$_用来包含这些行一样,我们不必手动将正则表达式绑定到字符串。因此,我们可以写:

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::Findchdir进入正确的目录。这使我们的处理变得更加容易。如果要拥有目录,请使用$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循环。重命名文件后,也无需检查其他行。

于 2013-07-25T06:11:34.573 回答