【问题标题】:Perl script to print the last modified subdirectory in a directory用于打印目录中最后修改的子目录的 Perl 脚本
【发布时间】:2014-03-25 23:57:21
【问题描述】:

我正在编写一个 Perl 脚本来打印目录中最后修改的子目录。

例如目录结构如下

amr/lex/
amr/kik/
amr/rtr/
amr/rtr4/
apr/rtr6tyh/
amr/rtr6yhu/
amr/d5tyh/
amr/d5kuk/
..
..
..

amr 中的所有这些目录,例如 lexkikrtrrtr4rtr6tyhrtr6yhud5yh 等都有子目录

我必须在其中打印最后修改的子目录

例如

amr/lex 有 2 个目录 s1.0s2.0。我必须打印最后修改的日期和时间。

目前为止有这个

#!/usr/bin/perl -w

use strict;

my $path = '/main/amr';
my $directory;
my @sub_dir;
my $var;


opendir ($directory, $path);

@sub_dir = readdir($directory);

print "@sub_dir";

foreach $var (@sub_dir)
 {
  opendir (my $temp, $var);
  my @arr = readdir ($temp); 
  print "@arr\n";
 }

我有以下脚本 但它的行为不稳定,它为某些人打印最新修改,但对其他人不打印

use strict;

my $path = '***<specify your own path >***';
my $directory;
my @sub_dir;
my $var;
my $var1;

my $now = time();
my $curr = 0;  # most recent modified time
my $latestDir; # most recent sub dir

my $temp;

opendir ($directory, $path);

@sub_dir = readdir($directory);

foreach $var (@sub_dir) # dirA, dirB
{

#print "Test123 ". $path ."/". $var . "\n"; (for debug)

if($var ne "." && $var ne "..") {
      opendir ($temp, $path ."/". $var);
      my @arr = readdir ($temp);



   foreach $var1 (@arr) { # subdir1
     if($var1 ne "." && $var1 ne "..") {
       my @stats = stat($var1); #call method stat on each subdir stat[9] - latest modified time

       #print $var1 . "\n";
         if($now-$stats[9] < $now-$curr || $curr == 0) {
             $curr = $stats[9];
             $latestDir = $var1;
     }
    }
 } # end foreach
print $latestDir . "                   " .  $var  . "\n";
}
}# end foreach

知道这里出了什么问题... 非常感谢任何帮助

【问题讨论】:

  • 您似乎缺少use File::Find;,并且您没有使用其中的代码。
  • 感谢您的回复,我正在尝试读取目录,将其存储到数组中并从那里开始......不确定这是否是正确的方法..
  • @Miller:当你编辑别人的帖子时,我认为你应该说的不仅仅是“在正文中添加了 46 个字符”
  • @JonathanLeffler:OP 说他们想要“打印目录中最后修改的子目录”。使用File::Find 这样做会很尴尬且没有必要
  • 请举例说明你想要的输出

标签: perl


【解决方案1】:

这似乎使用File::Find

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

$ARGV[0] = "." unless scalar @ARGV;
my $new_name = $ARGV[0];
my @info = stat $new_name;
my $new_time = $info[9];

sub wanted
{
    my(@info) = lstat($_);
    my($mod_time) = $info[9];
    if (-d _ && $mod_time > $new_time)
    {
        $new_name = $File::Find::name;
        $new_time = $mod_time;
    }
    return;
}

find(\&wanted, @ARGV);
print "$new_time: $new_name\n";

这是 Perl 所以 TMTOWTDI - 有不止一种方法可以做到这一点。可能还有一些方法可以优化代码,或者让它更简洁。

它给出了合理的答案,例如:

1395633608: .

然后当我在(预先存在的)子目录 SHA-256 中创建一个文件时,它列出了:

1395633641: ./SHA-256

然后当我使用git 签入更改时,它给出了:

1395633722: ./.git

使用Borodin 在他的comment 中建议的问题的不同解释,您可以修改上面的脚本以使用按目录名称索引的哈希,条目是哈希引用,其中引用的哈希具有键@ 987654330@(直接子目录的名称)和mod_time(识别的子目录的修改时间)。

我在 Mac OS X 10.9.2 Mavericks 上使用 Perl 5.18.2,但除非您的 Perl 早于 5.12,否则应该没问题。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

$ARGV[0] = "." unless scalar @ARGV;

# dir_mod_times is indexed by directory and contains hash refs
# with keys sub_dir and mod_time.
my %dir_mod_times = ();

sub wanted
{
    my(@info) = lstat($_);
    my($mod_time) = $info[9];
    if (-d _)
    {
        my $ref = { sub_dir => $_, mod_time => $mod_time };
        $dir_mod_times{$File::Find::dir} //= $ref;
        $dir_mod_times{$File::Find::dir}   = $ref
            if ($mod_time > $dir_mod_times{$File::Find::dir}->{mod_time});
    }
    return;
}

find(\&wanted, @ARGV);

foreach my $dir (sort keys %dir_mod_times)
{
    printf "%d: %s/%s\n", $dir_mod_times{$dir}->{mod_time},
           $dir, $dir_mod_times{$dir}->{sub_dir};
}

样本输出:

1395635479: ./.
1395633721: ./.git/objects
1384144591: ./.git/logs/refs
1395598709: ./.git/logs/refs/heads
1395598709: ./.git/logs/refs/heads/so
1395633721: ./.git/objects/02
1395633722: ./.git/refs/heads
1395598709: ./.git/refs/heads/so
1384651972: ./SHA-256/so-20020953-sha256.dSYM
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents/Resources
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents/Resources/DWARF
1395629945: ./time.dSYM/Contents
1395629397: ./time.dSYM/Contents/Resources
1395629945: ./time.dSYM/Contents/Resources/DWARF

然后当我删除SHA-256/junk(一个空文件)时:

1395635569: ./SHA-256
1395633721: ./.git/objects
1384144591: ./.git/logs/refs
1395598709: ./.git/logs/refs/heads
1395598709: ./.git/logs/refs/heads/so
1395633721: ./.git/objects/02
1395633722: ./.git/refs/heads
1395598709: ./.git/refs/heads/so
1384651972: ./SHA-256/so-20020953-sha256.dSYM
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents/Resources
1384651972: ./SHA-256/so-20020953-sha256.dSYM/Contents/Resources/DWARF
1395629945: ./time.dSYM/Contents
1395629397: ./time.dSYM/Contents/Resources
1395629945: ./time.dSYM/Contents/Resources/DWARF

【讨论】:

  • 您可能比我更了解 OP,但您的解决方案仅解决了问题的标题。在我看来,他们想要/main/amr 的每个子目录中最年轻的子目录。无论如何都要 +1
  • @Borodin:我现在看到你对这个问题的解释与我不同。我认为这可以通过哈希和其他适当的更改来完成。
【解决方案2】:

这有点棘手,因为它取决于您对目录的修改时间的定义。目录(像所有文件一样)有一个与之关联的 mtime。这是最后一次修改目录。比如我在一个目录中添加了一个文件,该目录被修改了。如果我修改目录中的文件,它不会更新目录的修改时间,因为目录本身没有改变。

那么,您是在严格地谈论目录的修改时间,还是在谈论该目录(甚至可能是该目录的子目录)中的任何内容被修改的时间?比如我修改了那个目录下的一个文件,即使目录没有改变,我是否应该把它作为最后一次修改目录?

走简单的路,我们就简单说说ls -l命令上报的目录修改时间。

首先,在您的脚本中,您需要检查opendir 命令的状态以查看您是否已成功打开目录,或者使用use autodie; 自动die无法打开目录。我将使用use autodie;,因为这是现在推荐的做事方式。

您还可以使用-M test 返回上次修改目录时的天数(以小数部分表示)。比使用stat 获得mtime 要容易得多。

我还建议在您真正需要变量时使用my 声明变量,而不是在程序的最开始时声明变量,就像您使用 Cobol 或 Pascal 编写一样。带有my 的变量落入和超出范围,这可能是变量的有用属性。例如,如果我在循环中定义了一个变量,那么一旦我离开循环,该变量将不复存在。

这就是我所做的:

#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
use autodie;

my $directory = ".";

opendir ( my $dir_fh, $directory);

my $youngest_date;
my $youngest_subdir;
while ( my $subdir = readdir $dir_fh ) {
    next unless -d $subdir;
    next if $subdir eq "." or $subdir eq "..";

    # If this is the first subdirectory, set it as the youngest and go to the next

    if ( not defined $youngest_subdir ) {
        $youngest_date = -M $subdir;
        $youngest_subdir = $subdir;
        next;
    }

    # See if this subdirectory is younger than the youngest found so far

    if ( $youngest_date > -M $subdir ) {
        $youngest_date = -M $subdir;
        $youngest_subdir = $subdir;
    }
}

if ( $youngest_subdir ) {
    say "Youngest Subdirectory is $youngest_subdir.";
}
else {
    say "No subdirectories found.";

请注意,我在循环之外定义了 $youngest_date$youngest_subdir。这样,它们将存在于循环的每个交互中,并且一旦循环完成。但是,$subdir 本身将在循环的每次迭代中不复存在(再次创建它),并在我的循环完成后完全消失。

我要做的第一件事是跳过任何不是目录的条目 (next unless -d $subdir;) 并跳过 ... 目录。您可能想跳过任何以句点开头的目录。

一旦我有了一个目录,我就使用-M 命令获取它的年龄,并对照$youngest_date 进行检查,这是我找到的最年轻的修改日期。还要注意,当我获得第一个目录 (if ( not defined $youngest_subdir ) {) 时,我必须处理初始条件。

我还必须验证是否找到了一个子目录,然后才能打印出我的结果。

【讨论】:

  • 你的代码看起来很有希望,我该如何运行你的脚本?
【解决方案3】:

您的程序打开目录/main/amr,然后尝试将该目录中的所有内容作为另一个目录打开。

我希望您想要的只是/main/amr 中的目录列表,在这种情况下,这个简短的程序会有所帮助。它使用File::Spec::Functions为目录的每个成员构建完整的文件路径,比使用join更清晰、更便携

use strict;
use warnings;

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

my $path = '/main/amr';

opendir my ($dh), $path;

while (my $node = readdir $dh) {
  my $fullpath = catfile($path, $node);
  print "$fullpath\n" if -d $fullpath;
}

【讨论】:

  • 这列出了目录;我没有看到修改时间的测试。
  • @JonathanLeffler:没有。我想确定我与 OP 代码的偏差在增强之前是有效的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 2014-05-26
  • 2016-10-09
  • 1970-01-01
  • 2018-04-04
  • 1970-01-01
相关资源
最近更新 更多