我有一些目录,其名称是时间戳,自 1970 年 1 月 1 日起以毫秒为单位:
1439715011728
1439793321429
1439879712214
.
.
我需要一个像这样的输出:
1442039711 Sat Sep 12 08:35:11 CEST 2015
1442134211 Sun Sep 13 10:50:11 CEST 2015
1442212521 Mon Sep 14 08:35:21 CEST 2015
.
.
我可以通过命令列出所有目录:
find ./ -type d | cut -c 3-12
但我无法将输出放入下一个命令:date -d @xxxxxx
并操纵输出。
我怎样才能做到这一点?
答案1
您走在正确的轨道上(对于更简单的解决方案,仅运行 2 或 3 个命令,请参见下文)。您应该使用*
而不是./
摆脱当前目录1,这在某种程度上简化了毫秒的切割,然后只需将结果通过管道传输到GNUparallel
或xargs
2:
find * -type d | cut -c 1-10 | parallel date --date=@{} +%c
要得到
Sat 12 Sep 2015 08:35:11 CEST
Sun 13 Sep 2015 10:50:11 CEST
Mon 14 Sep 2015 08:35:21 CEST
并在其之前添加秒偏移量,如您的示例所示:
find * -type d | cut -c 1-10 | parallel 'echo "{} " $(date --date=@{} +%c)'
或者:
find * -type d | cut -c 1-10 | xargs -I{} bash -c 'echo "{} " $(date --date=@{} +%c)'
要得到:
1442039711 Sat 12 Sep 2015 08:35:11 CEST
1442134211 Sun 13 Sep 2015 10:50:11 CEST
1442212521 Mon 14 Sep 2015 08:35:21 CEST
然而,这样做更简单:
find * -type d -printf "@%.10f\n" | date -f - +'%s %c'
这会再次为您提供相同的请求输出。
使用的缺点*
是您的扩展受到命令行的限制,但优点是您可以按时间戳值对目录进行排序。如果目录数量有问题,请使用-mindepth 1
,但会丢失顺序:
find ./ -mindepth 1 -type d -printf "@%.10f\n" | date -f - +'%s %c'
sort
并根据需要插入:
find ./ -mindepth 1 -type d -printf "@%.10f\n" | sort | date -f - +'%s %c'
1这假设没有嵌套子目录,就像您的示例中的情况一样。您还可以使用²./ -mindepth 1
代替*
²您可以按照 @hobbs 和 @don_crissti 的建议在此处替换parallel
为xargs -I{}
,它只是更冗长。
³基于 Gilles 对使用date
s 文件读取功能的回答
答案2
我会避免在循环中为每个文件运行多个命令。由于您已经在使用 GNUisms:
find . ! -name . -prune -type d |
awk '{t = substr($0, 3, 10); print t, strftime("%a %b %d %T %Z %Y", t)}'
它只运行两个命令。strftime()
是 GNU 特定的,例如date -d
.
答案3
你已经拥有了:
find ./ -type d | cut -c 3-12
这可能会为您提供纪元格式的时间戳。现在添加一个 while 循环:
find ./ -type d | cut -c 3-12 | while read datestamp
do
printf %s "$datestamp"
date -d "@$datestamp"
done
但请注意,在某些 shell 中,该语法在子 shell 中获取 while 循环,这意味着如果您尝试在那里设置变量,那么一旦离开循环,该变量将不可见。为了解决这个问题,你需要稍微扭转一下局面:
while read datestamp
do
printf %s "$datestamp"
date -d "@$datestamp"
done < <(find ./ -type d | cut -c 3-12)
它将 放入find
子 shell 中,并将 while 循环保留在主 shell 中。不过,仅当您希望重用循环内部的结果时,才需要该语法( AT&Tksh
和特定语法)。zsh
bash
答案4
我会这样做 - 输入时间戳列表:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
while ( my $ts = <DATA> ) {
chomp ( $ts );
my $t = Time::Piece->new();
print $t->epoch, " ", $t,"\n";
}
__DATA__
1442039711
1442134211
1442212521
这输出:
1442039711 Sat Sep 12 07:35:11 2015
1442134211 Sun Sep 13 09:50:11 2015
1442212521 Mon Sep 14 07:35:21 2015
如果您想要特定的输出格式,您可以使用strftime
例如:
print $t->epoch, " ", $t->strftime("%Y-%m-%d %H:%M:%S"),"\n";
将其变成管道中的一个内衬:
perl -MTime::Piece -nle '$t=Time::Piece->new($_); print $t->epoch, " ", $t, "\n";'
但我可能建议改为使用该File::Find
模块并在 perl 中完成整个操作。如果您在剪切之前给出目录结构的示例,我会给您一个示例。但它会是这样的:
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
use File::Find;
sub print_timestamp_if_dir {
#skip if 'current' item is not a directory.
next unless -d;
#extract timestamp (replicating your cut command - I think?)
my ( $timestamp ) = m/.{3}(\d{9})/; #like cut -c 3-12;
#parse date
my $t = Time::Piece->new($timestamp);
#print file full path, epoch time and formatted time;
print $File::Find::name, " ", $t->epoch, " ", $t->strftime("%Y-%m-%d %H:%M:%S"),"\n";
}
find ( \&print_timestamp_if_dir, "." );