如何循环遍历perl中的目录

How to loop through a directory in perl

我有一个包含数百个文件的目录dir1,这些文件将由名为 HRest 的语音程序迭代处理。该程序应该一个接一个地获取每个文件,对其进行处理并将其放入一个新目录中(如 dir2 用于第一次迭代)以供下一次迭代使用。我的问题是我不知道我在 dir1 中循环遍历文件的方式以及我在 运行 脚本 (trainhmms.pl dir1 1) 中的方式是否正确。

如果dir1中的文件是L1,L2,L3,...,L500,我希望HRest执行为

HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l dir1/L1 -M dir2 -S train.scp

为第一个文件,并为

HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l dir1/L2 -M dir2 -S train.scp

下一个文件,所有文件依此类推。然后在下一次调用脚本时,我希望将其更改为

HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l dir2/L1 -M dir3 -S train.scp

第一个文件,依此类推..

这是第一次迭代的脚本:

#!/usr/bin/perl
use File::Slurp;

# Usage: trainhmms.pl dir1 1
# dir1:  Folder containing models after being initialised by HInit (L1,L2,..,L512)

$file = $ARGV[0];
$iter = $ARGV[1];


my @files = read_dir '/Users/negarolfati/Documents/Detection_rerun/AF_TIMIT/1_state//trainHMMs/dir1';

for my $file ( @files ) {


    $iter2 = $iter+1;
    $cmd = "HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l '$dir[$iter]/$file' -M '$dir[$iter2]' -S train.scp ";

    system("$cmd");

}

您不能只在目录字符串上使用 readdir。您必须 opendir 字符串,然后 readdir 从您获得的目录句柄,最后 closedir 句柄。

您还必须记住 readdir returns 目录名和文件名,以及伪目录 ... 。要仅过滤掉文件,您可以使用 -f 测试运算符。通常最方便的是 chdir 到您正在阅读的目录,这样您就不必在进行测试之前将路径附加到 readdir returns 的每个文件名。

我不知道 HRest 是什么,但是如果您的命令行必须从特定的工作目录执行(可能访问 timedlabels_train.mlftrain.scp)那么请说所以。我将不得不删除 chdir 语句。

这样的事情应该会让你继续下去。我使用了 autodie,它会自动检查文件系统操作。它节省了每次使用 or die $!.

显式检查 chdiropendir 的麻烦
#!/usr/bin/perl

use strict;
use warnings;
use autodie;

use File::Spec::Functions 'catdir';

my ($file, $iter) = @ARGV;

my $root = '/Users/negarolfati/Documents/Detection_rerun/AF_TIMIT/1_state/trainHMMs';
my $dir1 = catdir $root, 'dir'.$iter;
my $dir2 = catdir $root, 'dir'.($iter+1);

chdir $dir1;

opendir my ($dh), '.';
my @files = grep -f, readdir $dh;
closedir $dh;

for my $file ( @files ) {

    my $cmd = "HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l '$dir1/$file' -M '$dir2' -S train.scp";

    system($cmd);
}

更新

这是一个避免chdir的替代版本,因此当前工作目录保持不变。

我已经添加了您 bash 脚本中的辅助循环。我还添加了一个 print 语句,以便您可以在执行之前看到每个命令。

要允许 system 调用继续进行,只需删除或注释掉 next 语句。

#!/usr/bin/perl

use strict;
use warnings;
use autodie;

use File::Spec::Functions qw/ catdir catfile /;

STDOUT->autoflush;

my $root = '/Users/negarolfati/Documents/Detection_rerun/AF_TIMIT/1_state/trainHMMs';

for my $iter (1 .. 4) {

  my $dir1 = catdir $root, 'dir'.$iter;
  my $dir2 = catdir $root, 'dir'.($iter+1);

  opendir my ($dh), $dir1;

  while (my $node = readdir $dh) {
    my $file = catfile($dir1, $node);
    next unless -f $file;

    my $cmd = "HRest -T 1 -I timedlabels_train.mlf -t -i 20 -l '$file' -M '$dir2' -S train.scp";
    print $cmd, "\n";
    next;               # Remove for full functionality

    system($cmd);
  }

  closedir $dh;
}

你可以这样做:

my @files = <$path/*>;
foreach my $filename ( reverse(@files) ) {
...
}