文件未在同一文件夹中重命名

时间:2013-07-25 05:21:39

标签: perl

我正在尝试使用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 $!; 
        }
     }
   }
  } 
 }

1 个答案:

答案 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 ) 
{ ... }

啊,从哪里开始?

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

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

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

    这意味着像... or die "Can't open $file: $!"

  3. 不要将整个文件读入数组并循环遍历 。相反,使用类似while(<>)的循环,可以节省内存并迭代。这一次只能读取一行,这要好得多。

  4. 组合,这看起来像

    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 循环。重命名文件后,也无需检查其他行。