如何在目录中找到最新创建的文件?

Posted

技术标签:

【中文标题】如何在目录中找到最新创建的文件?【英文标题】:How can I find the newest created file in a directory? 【发布时间】:2010-09-24 15:04:00 【问题描述】:

在 Perl 中是否有一种优雅的方式来查找目录中的最新文件(按修改日期最新)?

到目前为止,我正在搜索我需要的文件,并为每个文件获取其修改时间,将其推入一个包含文件名、修改时间的数组,然后对其进行排序。

一定有更好的办法。

【问题讨论】:

【参考方案1】:

如果您需要一个排序列表(而不仅仅是第一个,请参阅 Brian 的答案),您的方式是“正确”的方式。如果您不想自己编写该代码,请使用this

use File::DirList;
my @list = File::DirList::list('.', 'M');

就我个人而言,我不会使用 ls -t 方法 - 这涉及到分叉另一个程序并且它不可移植。几乎没有我所说的“优雅”!


关于 rjray 的解决方案手工编码的解决方案,我会稍微改变一下:

opendir(my $DH, $DIR) or die "Error opening $DIR: $!";
my @files = map  [ stat "$DIR/$_", $_ ]  grep(! /^\.\.?$/, readdir($DH));
closedir($DH);

sub rev_by_date  $b->[9] <=> $a->[9] 
my @sorted_files = sort rev_by_date @files;

在此之后,@sorted_files 包含排序列表,其中第 0 个元素是最新文件,每个元素本身都包含对 stat 的结果的引用,文件名本身在最后一个元素中:

my @newest = @$sorted_files[0];
my $name = pop(@newest);

这样做的好处是,如果需要,以后可以更轻松地更改排序方法。


编辑:这是一个更易于阅读(但更长)的目录扫描版本,它还确保仅将普通文件添加到列表中:

my @files;
opendir(my $DH, $DIR) or die "Error opening $DIR: $!";
while (defined (my $file = readdir($DH))) 
  my $path = $DIR . '/' . $file;
  next unless (-f $path);           # ignore non-files - automatically does . and ..
  push(@files, [ stat(_), $path ]); # re-uses the stat results from '-f'

closedir($DH);

注意:在readdir() 的结果上测试defined() 是因为如果您只测试if (my $file = readdir($DH)),名为“0”的文件会导致循环失败

【讨论】:

File::DirListls 都需要安装(至少在 Windows 上)。 ` 如果你想要 newest 然后使用 @l = File::DirList::list('.', 'M'); say $l[0][0][13] 注意大写 M。 我不会称他为“正确”的方式。对于包含许多文件的目录,这将是一个蛞蝓。 File::DirList::listls -t 都返回文件名和 目录名 brian - 不再叫'ls'了。 JFS - 我的新 Perl 版本不包括目录名。【参考方案2】:

您不需要将所有修改时间和文件名保存在一个列表中,而且您可能不应该这样做。您需要做的就是查看一个文件,看看它是否比您之前看到的最旧的文件更旧:


    opendir my $dh, $dir or die "Could not open $dir: $!";

    my( $newest_name, $newest_time ) = ( undef, 2**31 -1 );

    while( defined( my $file = readdir( $dh ) ) ) 
        my $path = File::Spec->catfile( $dir, $file );
        next if -d $path; # skip directories, or anything else you like
        ( $newest_name, $newest_time ) = ( $file, -M _ ) if( -M $path < $newest_time );
    

    print "Newest file is $newest_name\n";

【讨论】:

它不过滤目录名称。 由于 $newest_time 自动初始化为 0,-M $path 永远不会少。你可以这样初始化它:$newest_time = 2**31 - 1 您发现了一个真正的问题,但原因是错误的。 -M 在程序启动后修改文件时为负数。如果尚未定义,我应该设置最新时间。 $newest_time 不会自动初始化为零:如果未定义,它会转换为 0,并且我以数字方式使用它(如 &lt; 运算符)。您也不想将其设置为某个神奇的值。在你拥有一个有意义的价值之前,你希望没有价值。 :) 这段代码还有一个bug:对于第一个文件-M _ 从stat缓存中返回一些随机值,因为-M $path没有被执行。 自从我发表评论后代码已经更正,您可以使用它@BramVanroy。【参考方案3】:

您可以尝试使用 shell 的 ls 命令:

@list = `ls -t`;
$newest = $list[0];

【讨论】:

仅适用于 UNIX(或带有 ls 命令的 Windows,例如 Cygwin),但它是一种更优雅的解决方案。 它适用于 Windows(使用 gnuwin32 实用程序)。但ls -t 同时返回文件和目录 名称。 这一点都不优雅。只是打字少了点。现在您需要为要检查的每个目录创建一个新进程。不好看。 :)【参考方案4】:

假设您知道要查看的$DIR

opendir(my $DH, $DIR) or die "Error opening $DIR: $!";
my %files = map  $_ => (stat("$DIR/$_"))[9]  grep(! /^\.\.?$/, readdir($DH));
closedir($DH);
my @sorted_files = sort  $files$b <=> $files$a  (keys %files);
# $sorted_files[0] is the most-recently modified. If it isn't the actual
# file-of-interest, you can iterate through @sorted_files until you find
# the interesting file(s).

包裹readdirgrep 过滤掉“。”和 ".." UNIX(-ish) 文件系统中的特殊文件。

【讨论】:

这不正是 Bonzo 所说的他正在做的事情吗? “搜索我需要的文件,并为每个文件获取它的修改时间,推入一个包含文件名、修改时间的数组,然后对其进行排序。”您只需将元组数组更改为哈希。【参考方案5】:

如果您不能让ls 像@Nathan 建议的那样为您进行排序,那么您可以通过只保留迄今为止看到的最新修改时间和相关文件名来优化您的流程,并在每次找到更新时替换它目录中的文件。无需保留任何您知道比您目前看到的最新文件更旧的文件,当然也无需对它们进行排序,因为您可以在从目录中读取时检测哪个是最新的。

【讨论】:

【参考方案6】:

主题是旧的,但也许有人会尝试它 - 它不是可移植的(仅限类 Unix 系统),但它非常简单且有效:

chdir $directory or die "不能改变目录";

我的 $newest_file = bash -c 'ls -t | head -1';

chomp $newest_file;

打印 "$newest_file \n";

【讨论】:

-1 我看不出把它变成一个 shell 脚本问题有什么好处,尤其是当建议的 shell 脚本试图解析 ls 的输出时。 Perl 能够很好地处理有问题的极端情况(带有换行符的文件名等)。

以上是关于如何在目录中找到最新创建的文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Linux的子目录中打开最新文件?

如何使用 .NET 在目录中查找最新文件且不循环?

如何找到修改文件的最新 git 提交?

如何递归查找目录中最新修改的文​​件?

如何在 C++ 中找到从 ofstream 创建的文件的目录路径?

如何编写 Windows 批处理脚本以从目录中复制最新文件?