【问题标题】:How to get last modified time of a file in perl in the format returned from diff command?如何以 diff 命令返回的格式获取 perl 中文件的最后修改时间?
【发布时间】:2019-10-03 08:43:14
【问题描述】:

我想以与 linux diff 命令显示的格式相同的格式显示时间。

D:\>diff -dub old.cfg new.cfg
--- old.cfg       2019-05-15 15:03:14.289950700 +0530
+++ new.cfg       2019-05-14 16:07:21.119695300 +0530

我想将文件的最后修改时间显示为2019-05-15 15:03:14.289950700 +0530
我使用了stat,但我没有得到纳秒部分。 Alo,需要给定格式的时区。我也尝试了Time::HiRes,但无法获得。

my $dt = strftime("%Y-%m-%d %H:%M:%S", localtime((stat($old_file))[9]));

它返回:2019-05-15 15:03:14。你能帮我追加其他信息吗?

【问题讨论】:

  • BSD::stat 看起来很有希望获得员工档案时间。
  • @Shawn:内部 perl 模块可以吗?
  • 我不这么认为。 stat 只返回秒,POSIX::stat 只是它的另一个名字。
  • 一旦你得到以纳秒为单位的时间......除以 1000000000 得到秒,使用它来获取时间作为人类可读的字符串,就像你正在做的那样,并使用余数来获取剩余的纳秒。

标签: perl diff stat strftime


【解决方案1】:

我必须首先说,这并不准确。即使您的硬件在返回到软件时可能支持纳秒级分辨率,但它也有数百倍的延迟。 (对于一个文件的时间戳,可能仅用于相对于其他文件,这可能不太重要。)

话虽如此,我发现声称纳秒的唯一方法是使用系统工具,所以stat

my ($ts) = grep { /^\s*Access: [0-9]{4}/ } qx(stat $file);
$ts =~ s/^\s*Access:\s+//;

或者,使用map 也作为过滤器

my ($ts) = map { /^\s*Access:\s+([0-9]{4}.*)/ ? $1 : () } qx(stat $file);

当然,这里的一个问题是我们必须解析输出;所以请查看您的手册页并测试您的stat,并且不要希望有太多的可移植性。注意系统变化,因为程序的输出格式可能会发生变化。但我没有在 Perl 中找到纳秒声明。

作为参考,在我的 CentOS 7 上(bashtcsh 下)

perl -we'$f=shift; print grep { /^\s*Access: [0-9]{4}/ } qx(stat $f)' file

打印,用于随机文件

访问:2019-05-15 13:21:57.723987422 -0700

再一次,“纳米”秒根本不准确。


另一个可能更可靠的输出解析选项是使用 ls 及其 --full-time

my $ts = join ' ', (split ' ', qx(ls --full-time $file))[5..7];

与往常一样,在运行外部命令时,请谨慎构建变量,并适当地引用和转义。一个好的工具是String::ShellQuote。或者,除非特别需要,否则最好完全避免使用 shell。最好使用运行外部工具的模块来完成所有这些工作。

【讨论】:

  • ls --full-time点赞
  • Time::HiRes's version of stat 将返回亚秒(如果有)。如果您确实必须使用外壳,则应使用 String::ShellQuote 引用您的文件名或绕过外壳解析,例如使用 IPC::ReadpipeX
  • @Grinnz 好吧,这无济于事——[Time::HiRes::stat] 说“无论如何不要指望纳秒级分辨率,甚至是微秒级分辨率。”运行外部命令,是的,当然,我希望他们做对了。我会添加评论
  • @KamalNayan 欢迎。我记得几年前ls 的手册页版本在错误部分“选项太多”之类的 :) 总是值得检查是否有什么东西
【解决方案2】:

这是一个多部分的挑战。第一个挑战是在文件时间上获得亚秒级的分辨率,这取决于操作系统和文件系统。第二部分是获取本地时区偏移量。然而,所有这些在现代系统上都是可行的。您与Time::HiRes 走在正确的轨道上,但strftime() 只会让您分道扬镳。这是一个示例程序,它将文件作为参数并根据您的要求打印高分辨率 mtime。

#!/usr/bin/perl

use strict;
use warnings;

use Time::HiRes qw(stat);
use Time::Piece;
use Time::Seconds;

sub timestr {
    my $t = localtime($_[0]);
    my $tstr = $t->strftime('%Y-%m-%d %H:%M:%S');
    if ($_[0] =~ /(\.\d+$)/) { $tstr .= $1 }
    my $off = $t->tzoffset;
    return sprintf('%s %+02d%02d', $tstr, int($off->hours), $off->minutes % 60);
}

for (@ARGV) {
    my @stat = stat($_);
    print "$_\n  ".($! ? "Error: $!" : timestr($stat[9]))."\n";
}
exit(0);

示例输出:

$ ./stat.pl stat.pl
stat.pl
  2019-05-16 20:02:38.76708 +1000

Time::Piece 正在完成很多繁重的工作,但是有一点点小技巧可以提取时间的小数部分,并将时区转换为所需的格式(我还没有对其进行了广泛的测试)。您可能希望在原始传入值上使用sprintf() 来强制指定特定的小数位数。我的代码只是诚实地再现了原始的小数部分。

请记住,并非所有时间戳都有小数部分,即使在支持亚秒级分辨率的文件系统上也是如此。例如,该文件可能是从没有亚秒级分辨率的源复制而来的。

【讨论】:

  • 回想起来,我认为最好通过使用 sprintf() 格式化传入的数字来启动 timestr() 函数,然后将其拆分到小数位。这使算术始终以整数为基础,并确保您有固定的小数位数。
猜你喜欢
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多