Perl-连接具有相似名称的文件模式,并将连接的文件名写入列表

Perl-连接具有相似名称的文件模式,并将连接的文件名写入列表,perl,concatenation,Perl,Concatenation,我有一个包含多个子目录的目录,每个子目录都有一组固定的文件——每个类别一个,如- 1)Main_dir 1.1) Subdir1 with files - Test.1.age.txt - Test.1.name.txt - Test.1.place.csv .......... 1.2) Subdir2 with files - Test.2.age.txt - Test.2.name.txt - Test.2.place.csv ......... 大约有20个文件夹,其

我有一个包含多个子目录的目录,每个子目录都有一组固定的文件——每个类别一个,如-

1)Main_dir
1.1) Subdir1 with files 

 - Test.1.age.txt
 - Test.1.name.txt
 - Test.1.place.csv
..........
1.2) Subdir2 with files 
 - Test.2.age.txt
 - Test.2.name.txt
 - Test.2.place.csv
.........
大约有20个文件夹,其中包含10个文件。我需要首先将每个类别下的文件(如Test.1.age.txt和Test.2.age.txt)连接到一个组合的.age.txt文件中,完成所有连接后,我想将这些文件名打印到一个新的Final_list.txt文件中,如

./Main_dir/Combined.age.txt
./Main_dir/Combined.name.txt

我能够读取数组中所有子目录中的所有文件,但我不确定如何对相似的文件名进行模式搜索。此外,将能够找出这个打印出的部分代码。任何人都可以分享如何进行这种模式搜索的串联?到目前为止,我的代码是:

use warnings;
use strict;
use File::Spec;
use Data::Dumper;
use File::Basename;

foreach my $file (@files) {
print "$file\n";
}
my $testdir = './Main_dir';
my @Comp_list = glob("$testdir/test_dir*/*.txt");
我正在尝试对@Comp_列表中的数组内容进行模式搜索,我当然需要学习-

foreach my $f1 (@Comp_list) {
        if($f1 !~ /^(\./\.txt$/) {
        print $f1; # check if reading the file right 


 #push it to a file using concatfile( 
}}

非常感谢

我认为如果你先对文件进行分类,然后再处理它们会更容易

use warnings;
use strict;

use File::Spec;
use Data::Dumper;
use File::Basename;

my %hash = ();

my $testdir = './main_dir';
my @comp_list = glob("$testdir/**/*.txt");

foreach my $file (@comp_list){
    $file =~ /(\w+\.\d\..+\.txt)/;
    next if not defined $1;
    my @tmp = split(/\./, $1);
    if (not defined $hash{$tmp[-2]}) {
        $hash{$tmp[-2]} = [$file];
    }else{
        push($hash{$tmp[-2]}, $file);
    }
}

print Dumper(\%hash);
档案:

main_dir
├── sub1
│   ├── File.1.age.txt
│   └── File.1.name.txt
└── sub2
    ├── File.2.age.txt
    └── File.2.name.txt
结果:

$VAR1 = {
          'age' => [
                     './main_dir/sub1/File.1.age.txt',
                     './main_dir/sub2/File.2.age.txt'
                   ],
          'name' => [
                      './main_dir/sub1/File.1.name.txt',
                      './main_dir/sub2/File.2.name.txt'
                    ]
        };

您可以创建一个循环来连接和组合文件

我认为如果您先对文件进行分类,然后再使用它们,会更容易

use warnings;
use strict;

use File::Spec;
use Data::Dumper;
use File::Basename;

my %hash = ();

my $testdir = './main_dir';
my @comp_list = glob("$testdir/**/*.txt");

foreach my $file (@comp_list){
    $file =~ /(\w+\.\d\..+\.txt)/;
    next if not defined $1;
    my @tmp = split(/\./, $1);
    if (not defined $hash{$tmp[-2]}) {
        $hash{$tmp[-2]} = [$file];
    }else{
        push($hash{$tmp[-2]}, $file);
    }
}

print Dumper(\%hash);
档案:

main_dir
├── sub1
│   ├── File.1.age.txt
│   └── File.1.name.txt
└── sub2
    ├── File.2.age.txt
    └── File.2.name.txt
结果:

$VAR1 = {
          'age' => [
                     './main_dir/sub1/File.1.age.txt',
                     './main_dir/sub2/File.2.age.txt'
                   ],
          'name' => [
                      './main_dir/sub1/File.1.name.txt',
                      './main_dir/sub2/File.2.name.txt'
                    ]
        };

您可以创建一个循环来连接和组合文件

这应该适合您。我只是对它进行了表面测试,因为创建一些测试数据需要一些时间,所以您手头有一些测试数据,我希望您能报告任何问题

该程序将通过等价的
glob
调用隔离找到的所有文件,并根据它们的类型将它们放入存储桶中。我假设名称与您显示的完全相同,因此当文件名被拆分为点时,类型是倒数第二个字段;i、 e.
Test.1.age.txt的类型是
age

收集了所有文件列表后,我使用了一种最初设计用于读取命令行上指定的所有文件的技术。如果将
@ARGV
设置为文件列表,则
操作将读取所有文件,就像它们是一个文件一样,因此可以轻松地复制到新的输出文件

如果您需要按照特定顺序连接文件,那么我必须修改我的解决方案。目前,它们将按照
glob
返回它们的顺序进行处理——可能是按照它们的文件名的词法顺序,但您不应该依赖于此

use strict;
use warnings 'all';
use v5.14.0;    # For autoflush method

use File::Spec::Functions 'catfile';

use constant ROOT_DIR => './Main_dir';

my %files;

my $pattern = catfile(ROOT_DIR, 'test_dir*', '*.txt');

for my $file ( glob $pattern ) {
    my @fields = split /\./, $file;
    my $type = lc $fields[-2];
    push @{ $files{$type} }, $file;
}

STDOUT->autoflush;    # Get prompt reports of progress

for my $type ( keys %files ) {

    my $outfile = catfile(ROOT_DIR, "Combined.$type.txt");
    open my $out_fh, '>', $outfile or die qq{Unable to open "$outfile" for output: $!};

    my $files = $files{$type};

    printf qq{Writing aggregate file "%s" from %d input file%s ... },
            $outfile,
            scalar @$files,
            @$files == 1 ? '' : 's';

    local @ARGV = @$files;
    print $out_fh $_ while <ARGV>;

    print "complete\n";
}
使用严格;
使用“全部”警告;
使用v5.14.0;#自动冲洗法
使用File::Spec::Functions'catfile';
使用常量ROOT_DIR=>'./Main_DIR';
我的%s文件;
my$pattern=catfile(ROOT_DIR、'test_DIR*'、'*.txt');
对于我的$文件(glob$模式){
my@fields=split/\./,$file;
my$type=lc$字段[-2];
推送@{$files{$type}},$file;
}
标准输出->自动刷新;#及时获取进度报告
对于my$类型(键%files){
my$outfile=catfile(ROOT_DIR,“Combined.$type.txt”);
打开我的$out_fh,'>',$outfile或死亡qq{无法打开输出的“$outfile:$!};
my$files=$files{$type};
printf qq{正在从%d个输入文件%s写入聚合文件“%s…”,
$outfile,
标量@$文件,
@$files==1?“”:“s”;
本地@ARGV=@$文件;
打印$out\u fh$\u同时;
打印“完成”\n;
}

这应该适合您。我只是对它进行了表面测试,因为创建一些测试数据需要一些时间,所以您手头有一些测试数据,我希望您能报告任何问题

该程序将通过等价的
glob
调用隔离找到的所有文件,并根据它们的类型将它们放入存储桶中。我假设名称与您显示的完全相同,因此当文件名被拆分为点时,类型是倒数第二个字段;i、 e.
Test.1.age.txt的类型是
age

收集了所有文件列表后,我使用了一种最初设计用于读取命令行上指定的所有文件的技术。如果将
@ARGV
设置为文件列表,则
操作将读取所有文件,就像它们是一个文件一样,因此可以轻松地复制到新的输出文件

如果您需要按照特定顺序连接文件,那么我必须修改我的解决方案。目前,它们将按照
glob
返回它们的顺序进行处理——可能是按照它们的文件名的词法顺序,但您不应该依赖于此

use strict;
use warnings 'all';
use v5.14.0;    # For autoflush method

use File::Spec::Functions 'catfile';

use constant ROOT_DIR => './Main_dir';

my %files;

my $pattern = catfile(ROOT_DIR, 'test_dir*', '*.txt');

for my $file ( glob $pattern ) {
    my @fields = split /\./, $file;
    my $type = lc $fields[-2];
    push @{ $files{$type} }, $file;
}

STDOUT->autoflush;    # Get prompt reports of progress

for my $type ( keys %files ) {

    my $outfile = catfile(ROOT_DIR, "Combined.$type.txt");
    open my $out_fh, '>', $outfile or die qq{Unable to open "$outfile" for output: $!};

    my $files = $files{$type};

    printf qq{Writing aggregate file "%s" from %d input file%s ... },
            $outfile,
            scalar @$files,
            @$files == 1 ? '' : 's';

    local @ARGV = @$files;
    print $out_fh $_ while <ARGV>;

    print "complete\n";
}
使用严格;
使用“全部”警告;
使用v5.14.0;#自动冲洗法
使用File::Spec::Functions'catfile';
使用常量ROOT_DIR=>'./Main_DIR';
我的%s文件;
my$pattern=catfile(ROOT_DIR、'test_DIR*'、'*.txt');
对于我的$文件(glob$模式){
my@fields=split/\./,$file;
my$type=lc$字段[-2];
推送@{$files{$type}},$file;
}
标准输出->自动刷新;#及时获取进度报告
对于my$类型(键%files){
my$outfile=catfile(ROOT_DIR,“Combined.$type.txt”);
打开我的$out_fh,'>',$outfile或死亡qq{无法打开输出的“$outfile:$!};
my$files=$files{$type};
printf qq{正在从%d个输入文件%s写入聚合文件“%s…”,
$outfile,
标量@$文件,
@$files==1?“”:“s”;
本地@ARGV=@$文件;
打印$out\u fh$\u同时;
打印“完成”\n;
}

STDOUT->autoflush
是否需要模块?(当然,这是一个很好的答案)@zdim:
autoflush
来自。它的子类,如果您使用的是PerlV5.14.0或laterOh。。。非常感谢你!我不知道。(我们在这里运行v5.10:(.非常感谢@Borodin,您的解决方案在我当前的一组文件上运行得非常好,但是正如您所指出的,如果我需要按照文件名的顺序排列,比如Test1.age.txt、Test2.age.txt、Test3.age.txt,我是否应该在文件名上运行循环并使用正则表达式检查数字?非常感谢!!
STDOUT->autoflush
需要模块吗?(当然,这是一个很好的答案)@zdim:
autoflush
来自它的subc