如何链接命令“ date -d @xxxxxx”和“ find ./”?


14

我有名称为时间戳的目录,自1970-01-01起以毫秒为单位:

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并操纵输出。

我怎样才能做到这一点?


2
这些时间戳如何转换为时代?因为您的电话号码太长了……(第一个-是Fri Oct 2 05:35:28 47592
Sobrique

1
@Sobrique距时代已过去毫秒。
吉尔斯(Gillles)“所以-别再邪恶了”

Answers:


10

您处在正确的轨道上(对于更简单的解决方案,仅运行2或3个命令,请参见下文)。您应该使用*代替./摆脱当前目录¹ ,这在某种程度上简化了毫秒的切割,然后将结果通过管道传递到GNU parallelxargs²中:

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'

¹ 假设没有嵌套的子目录,如您的示例所示。您也可以使用./ -mindepth 1代替*
²,您可以parallelxargs -I{}此处替换,如@hobbs和@don_crissti所建议的那样,它只是比较冗长。 ³ 基于Gilles使用date文件读取功能的答案


或者,xargs如果您没有parallel,很多人可能没有。
hobbs

@hobbs AFAIK xargs其中争论还在继续像没有指定选项parallel{}
Anthon

4
它确实:find ./ -type d | cut -c 3-12 | xargs -I{} date --d @{} +'%Y-%m-%d'
don_crissti

如果使用该-I选项,@ Anthon会执行。
hobbs

1
@Anthon,可以缩写GNU长选项,只要它们没有歧义即可。 --d--da可以与当前版本的GNU一起使用date,但在date引入--dalek选项(对于Dalek日历中的日期)这一天可能会停止工作。
斯特凡Chazelas

10

我会避免在一个循环中为每个文件运行多个命令。由于您已经在使用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


这不会减少目录名称的毫秒数,而是显示完整的13个字符,而不是所请求的前10个字符
-Anthon

@Anthon,是的,错过了这个要求。现在应该可以。
斯特凡Chazelas

8

你已经有了:

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)

它将findwhile 放在子shell中,并将while循环保留在主shell中。不过ksh,仅当您希望从循环内部重用结果时,才需要使用该语法(AT&T zshbash特定语法)。


无论如何,说这是针对bash的说法都是不正确的:)
Wouter Verhelst

实际上,正如您最初编写done <(find)的那样done < <(find),它不是而是正确的yash(在哪里<(...)是进程重定向,而不是进程替换),所以我的编辑有点麻烦,因为它可能是您想要的外壳。
斯特凡Chazelas

6

如果您有GNU日期,它可以转换从输入文件读取的日期。您只需要稍微修饰一下时间戳记即可识别它们。基于Unix纪元的时间戳的输入语法@后跟秒数,该秒数可以包含小数点。

find ./ -type d ! -name '*[!0-9]*' |
sed -e 's~.*/~@~' -e 's~[0-9][0-9][0-9]$~.&~' |
date -f - +'%s  %c'

+1用于使用dates文件读取。date: invalid date ‘@’由于当前目录(./)的翻译,将给出一个。而且由于您可以舍弃毫秒,因此可以简化第二次sed编辑,只删除最后3个字符。或删除所有内容并使用find * -type d -printf "@%.10f" | date ...
Anthon

5

我会完美地做到这一点-输入时间戳列表:

#!/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, "." ); 

2

zsh strftime内置:

zmodload zsh/datetime
for d (*(/))
strftime '%s %a %b %d %T %Z %Y' $d

假设当前目录中的所有目录名称实际上都是纪元时间。
如果您弄清楚示例中的数字应如何处理(它们看起来更像是Leia公主和Luke Skywalker的出生日期的纪元时间...),则可以进行进一步的过滤/处理,例如,递归搜索至少匹配的目录名称10位数字,并根据前10位数字计算日期:

setopt extendedglob
zmodload zsh/datetime
for d (**/[0-9](#c10,)(/))
strftime '%s %a %b %d %T %Z %Y' ${${d:t}:0:10}

2

使用GNU Parallel:

find ./ -type d | cut -c 3-12 | parallel -k 'echo {} `date -d @{}`'

如果您可以接受\ t而不是空格:

find ./ -type d | cut -c 3-12 | parallel -k --tag date -d @{}

请注意,这parallel是用编写的perl。考虑到perlstrftime()操作员,这似乎有些过分。像perl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
斯特凡Chazelas

2
1.较短。2.您不需要学习Perl。
Ole Tange

1
它缩短了27%,但效率降低了几个数量级(大约是我进行的测试的800倍;考虑到它需要生成一个shell(您的shell,而不是/ bin / sh)和每行的date命令)并且对系统不友好,因为它会立即负担所有CPU的负担。而且您仍然需要学习parallel。IMO parallel是使CPU密集型任务并行化的好工具,但在此实际上不适用于此类任务。
斯特凡Chazelas

在很多情况下,效率都不是问题,它仍然是可以接受的解决方案,但是仍然值得一提性能问题,尤其是考虑到人们通常认为高性能的并行韵律时。
斯特凡Chazelas

0

通常,find命令可以与使用exec参数的任何命令链接。

在您的情况下,您可以这样做:

find . -type d | cut -c 3-12 | while read line
do
       echo -n "${line}  "
       date -d $line
done

0

使用Python(这是最慢的解决方案)

for i in $(ls -A); do echo $i | xargs python -c "from sys import argv;from time import strftime;from datetime import datetime;print datetime.fromtimestamp(float(argv[1][:-3])).strftime('%Y-%m-%d %H:%M:%S'),'---',argv[1]"; done

给出:

2015-08-30 08:48:59 --- 1440917339340
2015-08-31 08:00:22 --- 1441000822458
2015-09-01 08:00:32 --- 1441087232437
2015-09-01 16:48:43 --- 1441118923773
2015-09-02 08:00:11 --- 1441173611869
2015-09-03 08:00:32 --- 1441260032393
2015-09-04 08:00:21 --- 1441346421651

为什么不在python中做所有这些呢?而不是束缚一堆管道?
Sobrique

这会更好。我同意。
lukaz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.