使用Perl重命名目录中的文件

使用Perl重命名目录中的文件,perl,file,directory,rename,Perl,File,Directory,Rename,我想获取一个目录,对于所有电子邮件(*.msg)文件,请删除开头的“RE”。我有以下代码,但重命名失败 opendir(DIR, 'emails') or die "Cannot open directory"; @files = readdir(DIR); closedir(DIR); for (@files){ next if $_ !~ m/^RE .+msg$/; $old = $_; s/RE //; rename($old, $_) or print

我想获取一个目录,对于所有电子邮件(*.msg)文件,请删除开头的“RE”。我有以下代码,但重命名失败

opendir(DIR, 'emails') or die "Cannot open directory";
@files = readdir(DIR);
closedir(DIR);

for (@files){
    next if $_ !~ m/^RE .+msg$/;
    $old = $_;
    s/RE //;
    rename($old, $_) or print "Error renaming: $old\n";
}

如果您的
/emails
目录包含以下文件:

1.msg
2.msg
3.msg
然后,您的
@文件
将看起来像
('.','..,'1.msg',2.msg',3.msg')
,但是您的
重命名
需要像
'emails/1.msg'
'emails/2.msg'
这样的名称。因此,在重命名之前,您可以:

chdir('emails');
for (@files) {
    #...
}
您可能还需要检查
chdir
返回值

或者自己添加目录名:

rename('emails/' . $old, 'emails/' . $_) or print "Error renaming $old: $!\n";
# or rename("emails/$old", "emails/$_") if you like string interpolation
# or you could use map if you like map
您可能希望使用以下方法将目录读取和筛选结合起来:

甚至这个:

opendir(DIR, 'emails') or die "Cannot open directory";
for (grep { /^RE .+msg$/ } readdir(DIR)) {
    (my $new = $_) =~ s/^RE //;
    rename("emails/$_", "emails/$new") or print "Error renaming $_ to $new: $!\n";
}
closedir(DIR);
你似乎是在假装,而不是假装

底层的
readdir
系统调用只返回目录中的文件名,并将包括两个条目
。这将贯穿到Perl中的
readdir
函数,只是为了更详细地介绍mu的答案

或者,如果您正在以某种方式收集数组中的所有结果,那么使用
readdir
也没有多大意义

@files = glob('emails/*');

如前所述,脚本失败是因为预期的路径和脚本使用的路径不同

我建议使用更透明的方法。在我看来,硬编码目录不是一个好主意。有一天,我编写了一个脚本,用硬编码的路径修改一些原始文件,我的一位同事认为这是一个很好的脚本,可以借来修改他的副本。哎呀

用法:

perl script.pl "^RE " *.msg
use strict;
use warnings;
use v5.10;
use File::Copy qw(move);

my $rx = shift;   # e.g. "^RE "

if ($ENV{OS} =~ /^Windows/) {  # Patch for Windows' lack of shell globbing
    @ARGV = map glob, @ARGV;
}

for (@ARGV) {
    if (/$rx/) {
        my $new = s/$rx//r;  # Using non-destructive substitution
        say "Moving $_ to $new ...";
        move($_, $new) or die $!;
    }
}
i、 e.regex,然后是一个文件全局列表,其中的路径表示与脚本相关,例如
*.msg
emails/*.msg
甚至
/home/pat/emails/*.msg/home/foo/*.msg
。(可能有多个全局)

使用绝对路径将让用户毫无疑问地知道他将影响哪些文件,并且还将使脚本可重用

代码:

perl script.pl "^RE " *.msg
use strict;
use warnings;
use v5.10;
use File::Copy qw(move);

my $rx = shift;   # e.g. "^RE "

if ($ENV{OS} =~ /^Windows/) {  # Patch for Windows' lack of shell globbing
    @ARGV = map glob, @ARGV;
}

for (@ARGV) {
    if (/$rx/) {
        my $new = s/$rx//r;  # Using non-destructive substitution
        say "Moving $_ to $new ...";
        move($_, $new) or die $!;
    }
}

我不知道正则表达式是否适合文件的指定名称,但在一行中,这可以通过以下方法完成:

perl-E'for(){($new=$)=~s/(^RE)(.*$)/$2/;比如说$.->“$new}

say…
很适合测试,只需将其替换为
rename$\u$new
rename($\u$new)

  • 读取当前目录中的每个文件
  • ($new=$\u)=~
    将以下替换保存在
    $new
    中,并保持
    $\ucode>不变
  • (^RE)
    将此匹配项保存在$1(可选)中,只需在开始处将文件与“RE”匹配即可
  • (.*$)
    将所有内容保存到并包括行的结尾($)->到$2中
  • $2

  • 如果您打印错误($!),您可能会了解出了什么问题……感谢您指出这一点。错误是“没有这样的文件或目录”。我很惊讶,$old文件名是准确的。请注意,
    rename
    不提供跨平台支持,而
    file::Copy
    move
    功能does@Zaid:但它是否也保留权限,跨平台?非常好。感谢您解释这一点。+1提醒我存在
    glob
    glob
    的缺点是,由于
    'emails/'
    前缀的存在,它会使名称有点混乱,这是一个很小的问题,但处理起来很简单。嗨,我真的很喜欢这个想法。我的$new=s/$rx//r行似乎有错误。输出是“在运算符应位于“s/$rx//r”附近的位置找到的裸字,并且在same@JP. 哦,那可能是因为你的perl版本。。你使用哪个版本?@JP
    corelist“/r”
    为我提供了
    /r是与perl v5.8.9一起发布的,因此早期版本中没有非破坏性选项。@JP。那就行了。代码对我有用。您是否使用
    “^RE”
    作为正则表达式?正则表达式中不应该有斜杠。要么是这样,要么是复制/粘贴到您的终端时出错。您可以检查以确保您可以在您的版本中使用
    /r
    say
    。@JP。如果是
    /r
    选项,则很容易解决。只需将该行替换为
    my$new=$\$新=~s/$rx/