【问题标题】:uniquely rename each of many files using perl使用 perl 唯一地重命名许多文件中的每一个
【发布时间】:2013-07-18 19:53:16
【问题描述】:

我有一个包含 96 个要重命名的文件的文件夹。问题是每个文件名都需要一个唯一的更改......不像在每个名称的前面添加一个零或更改扩展名。进行搜索和替换是不切实际的。 以下是我要更改的名称示例:

newSEACODI-sww2320H-sww24_07A_CP.9_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07B_CP.10_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07C_CP.11_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07D_CP.12_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07E_R.1_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07F_R.3_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07G_R.4_sww2320H_sww2403F.fsa
newSEACODI-sww2320H-sww24_07H_R.5_sww2320H_sww2403F.fsa

我想用perl把上面的名字分别改成下面的名字:

SEACODI_07A_A.2_sww2320H_2403F.fsa
SEACODI_07B_A.4_sww2320H_2403F.fsa
SEACODI_07C_H.1_sww2320H_2403F.fsa
SEACODI_07D_H.3_sww2320H_2403F.fsa
SEACODI_07E_H.6_sww2320H_2403F.fsa
SEACODI_07F_H.7_sww2320H_2403F.fsa
SEACODI_07G_Rb.4_sww2320H_2403F.fsa
SEACODI_07H_Rb.9_sww2320H_2403F.fsa

这样的事情能做到吗?我有一个模糊的想法,我可能会制作一个包含新名称列表的文本文件,并将该列表称为@newnames。我会从当前文件名中创建另一个数组,并将其命名为@oldnames。然后我会做某种for 循环,其中@oldnames 中的每个元素$i 都被@newnames 中的相应$i 替换。

不过,我不知道如何从我当前的文件名中创建一个数组,因此我不确定这个模糊的想法是否正确。我将带有混乱名称的文件保存在一个名为“oldnames”的目录中。下面是我尝试从该目录中的文件名中创建一个数组:

#!/usr/bin/perl -w
use strict; use warnings;

my $dir = 'oldnames';
opendir ('oldnames', $dir) or die "cannot open dir $dir: $!";
my @file = readdir 'oldnames';
closedir 'oldnames';

print "@file\n";

上面似乎没有做任何事情。我迷路了。帮忙?

【问题讨论】:

  • 将问题拆分为 3 个部分: (1) 解析现有文件名以提取组件; (2) 设计一种转换,将旧文件名转换为所需的新格式,并用转换填充映射; (3) 编写代码进行重命名。如果您无法提出算法转换,那么您唯一的选择是提供新旧文件名的参数文件,您可以手动创建。
  • readdir的使用方法请看the manual
  • 说到策略,我建议让你的程序输出一个每个文件一行的脚本,如mv oldname newname,然后你可以在它看起来正确的时候运行它。
  • Bill:我不想为每个文件都写一个mv,因为总共大约有一百个。
  • Jim:不幸的是,没有我可以使用的算法。我有一个新旧文件名并排的电子表格。这就是参数文件的意思吗?另外:您能解释一下“使用转化填充地图”是什么意思吗?

标签: arrays perl rename file-rename


【解决方案1】:

这里:

#!/usr/bin/perl
use warnings;
use strict;

use autodie;
use File::Copy;

# capture script name, in case we are running the script from the 
# same directory we working on. 
my $this_file = (split(/\//, $0))[-1];
print "skipping file: $this_file\n";

my $oldnames = "/some/path/to/oldnames";
my $newnames = "/some/path/to/newnames";

# open the directory
opendir(my $dh, $oldnames); 

# grep out all directories and possibly this script. 
my @files_to_rename = grep { !-d && $_ ne $this_file } readdir $dh;
closedir $dh;

### UPDATED ###
# create hash of file names from lists:
my @old_filenames = qw(file1 file2 file3 file4);
my @new_filenames = qw(onefile twofile threefile fourfile);
my $filenames = create_hash_of_filenames(\@old_filenames, \@new_filenames);
my @missing_new_file = ();  


# change directory, so we don't have to worry about pathing
# of files to rename and move... 
chdir($oldnames);
mkdir($newnames) if !-e $newnames;


### UPDATED ###
for my $file (@files_to_rename) {
    # Check that current file exists in the hash,
    # if true, copy old file to new location with new name
    if( exists($filenames->{$file}) ) { 
        copy($file, "$newnames/$filenames->{$file}");
    } else {
        push @missing_new_file, $file;
    } 
}


if( @missing_new_file ) { 
    print "Could not map files:\n", 
        join("\n", @missing_new_file), "\n";
}

# create_hash_of_filenames: creates a hash, where
# key = oldname, value = newname
# input: two array refs 
# output: hash ref 
sub create_hash_of_filenames {
    my ($oldnames, $newnames) = @_; 
    my %filenames = (); 

    for my $i ( 0 .. (scalar(@$oldnames) - 1) ) { 
        $filenames{$$oldnames[$i]} = $$newnames[$i];
    }

    # see Dumper output below, to see data structure
    return \%filenames;
}

自卸车结果:

$VAR1 = {
  'file2' => 'twofile',
  'file1' => 'onefile',
  'file4' => 'fourfile',
  'file3' => 'threefile'
};

运行脚本:

$ ./test.pl 
skipping file: test.pl
Could not map files:
a_file.txt
b_file.txt
c_file.txt

文件结果:

$ ls oldnames/
a_file.txt
b_file.txt
c_file.txt
file1
file2
file3
file4

$ ls newnames/
fourfile
onefile
threefile
twofile

【讨论】:

  • 感谢您向我展示如何使用 File::Copy!这将非常方便。很抱歉,我对需要进行哪些更改含糊不清。我需要在每个文件名中更改一些不同的内容。我不能对所有名称应用相同的更改。我有一个每个名字应该是什么的列表,并且它们没有模式。这就像试图将名称列表(Betty、Sue、Billy、Bob)更改为列表(Andrew、Sophie、Reginald、Abigail),这样 Betty 就变成了 Andrew,Sue 变成了 Sophie,等等。
  • @user2585169 已更新,这可以解决您的命名问题。
  • +1 以获得严肃的解决方案。它甚至会检查脚本名称并且不会将其复制过来!干得好。我添加了一个凌乱的单线作为不安全的替代方案:-)
  • 谢谢,谢谢!如果我有正确的声誉,我会支持你,chrsblck。
【解决方案2】:

您的代码有点奇怪,但它应该可以工作。您是在“oldnames”目录还是在它上面的目录中运行它?你应该在它上面的目录中。更标准的写法是这样的:

#!/usr/bin/perl -w
use strict; use warnings;

my $dir = 'oldnames';
opendir ( my $oldnames, $dir) or die "cannot open dir $dir: $!";
my @file = readdir $oldnames;
closedir $oldnames;

print "@file\n";

这将使用oldnames 中的所有文件填充@files包括 '。'和 '..'。您可能需要根据重命名方式过滤掉这些内容。

【讨论】:

    【解决方案3】:

    你能用rename 做到这一点吗?如果我记得的话,它确实允许您使用 perl 代码和表达式作为参数。

    真正的答案是@chrsblck 的答案,它会进行一些检查并且不会造成混乱。

    为了比较,这里有一个凌乱的班轮可能就足够了。它依赖于您提供一个等效的新文件名列表,该列表将以正确的顺序重命名您的旧文件列表。也许对于您的情况(您不想对文件名进行任何编程转换),您可以只使用 shell 循环(见本文末尾)从文件中读取新旧名称列表。更好的perl 解决方案是将这些文件名列表中的both 放入两列,然后使用-a 开关、@FFile::Copy 复制文件大约。

    无论如何,以下是一些建议。

    首先,设置:

     % vim newfilenames.txt # list new names one per line corresponding to old names.
     % wc -l newfilenames.txt # the same number of new names as files in ./oldfiles/
      8 newfilenames.txt
     % ls -1 oldfiles  # 8 files rename these in order to list from newfilenames.txt
     newSEACODI-sww2320H-sww24_07A_CP.9_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07B_CP.10_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07C_CP.11_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07D_CP.12_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07E_R.1_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07F_R.3_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07G_R.4_sww2320H_sww2403F.fsa
     newSEACODI-sww2320H-sww24_07H_R.5_sww2320H_sww2403F.fsa
    

    如上安排文件,复制所有内容:

    perl -MFile::Copy -E 'opendir($dh , oldfiles); @newfiles=`cat newfilenames.txt`; chomp @newfiles; @oldfiles = sort grep(/^.+\..+$/, readdir $dh); END {for $i (0..$#oldfiles){copy("oldfiles/$oldfiles[$i]", "newfiles/$newfiles[$i]"); }}'
    

    不漂亮:你必须在@oldfiles 上使用grepsort 以摆脱. .. 并将数组元素按顺序排列。而且总是存在拼写错误可能会造成混乱并且很难弄清楚的风险。
    如果您将旧名称和新名称放在几个文件中,您可以使用 shell 脚本执行此操作:

    for i in `cat ../oldfilenames.txt` ; do ; done; for n in `cat ../newfilenames.txt`; do cp $i $n;
    

    或者只是 cd 进入包含旧文件的目录并执行以下操作:

    mkdir new
    for i in * ; do ; done; for n in `cat ../newfilenames.txt`; do cp $i new/$n;
    

    祝你好运!

    【讨论】:

    • 我看了rename。问题是我不太清楚要使用什么“规则”。我可以为其制定算法的名称更改没有模式。我基本上需要做的是用另一个等长数组的相应位置的字符串替换编号数组中的字符串......这有意义吗?
    • 是的 - 我误解了。你的问题可能会更简单 - 不需要正则表达式:)
    • 确实——但我不知道该怎么做。
    • @user2585169 我添加了一些perl 的示例,一个内衬和一个外壳循环应该可以完成您的任务。你应该接受@chrsblck 或我的回答,但无论你做什么,你都应该对我们俩投赞成票:-)
    • 如果可以的话,我会投票给你——看起来我还没有足够的声望点^-^
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2017-12-25
    • 2019-07-21
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多