Como encadear os comandos 'date -d @xxxxxx' e 'find ./'?

13

Eu tenho diretórios cujos nomes são timestamps, dados em milissegundos desde 1970-01-01:

1439715011728
1439793321429
1439879712214
.
.

E eu preciso de uma saída como:

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
.
.

Eu posso listar todos os diretórios por comando:

find ./ -type d | cut -c 3-12

Mas não posso colocar a saída no próximo comando: date -d @xxxxxx e manipular a saída.

Como posso fazer isso?

    
por BlaMa 15.09.2015 / 11:04

9 respostas

10

Você está no caminho certo (para uma solução mais simples, executando apenas 2 ou 3 comandos, veja abaixo). Você deve usar * em vez de ./ para se livrar do diretório atual¹ e isso simplifica um pouco o milésimo de segundo, então canalize o resultado para GNU parallel ou xargs ²:

find * -type d | cut -c 1-10 | parallel date --date=@{} +%c

para obter

Sat 12 Sep 2015 08:35:11 CEST
Sun 13 Sep 2015 10:50:11 CEST
Mon 14 Sep 2015 08:35:21 CEST

e para adicionar o deslocamento de segundos antes disso, conforme o exemplo indica:

find * -type d | cut -c 1-10 | parallel 'echo "{} "  $(date --date=@{} +%c)'

ou:

find * -type d | cut -c 1-10 | xargs -I{} bash -c 'echo "{} "  $(date --date=@{} +%c)'

para obter:

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

No entanto, é mais simples fazer³:

find * -type d -printf "@%.10f\n" | date -f - +'%s  %c'

que lhe dá a mesma saída solicitada mais uma vez.

A desvantagem de usar * é que você está limitado pela sua linha de comando para sua expansão, mas a vantagem é que você obtém seus diretórios classificados por valor de registro de data e hora. Se o número de diretórios for um problema, use -mindepth 1 , mas perca a ordem:

find ./ -mindepth 1 -type d -printf "@%.10f\n" | date -f - +'%s  %c'

e insira sort , se necessário:

find ./ -mindepth 1 -type d -printf "@%.10f\n" | sort | date -f - +'%s  %c'

¹ Isto assume que não há subdiretórios aninhados, como parece ser o caso do seu exemplo. Você também pode usar ./ -mindepth 1 em vez de *
² Você pode substituir parallel com xargs -I{} aqui como @hobbs e @don_crissti sugeriu, apenas mais detalhado. ³ baseado na resposta de Gilles para usar os recursos de leitura de arquivos date s

    
por 15.09.2015 / 11:23
10

Eu evitaria executar vários comandos por arquivo em um loop. Já que você já está usando o GNUisms:

find . ! -name . -prune -type d |
  awk '{t = substr($0, 3, 10); print t, strftime("%a %b %d %T %Z %Y", t)}'

Que apenas executa dois comandos. strftime() é específico do GNU, como date -d .

    
por 15.09.2015 / 11:43
8

Você já tem:

find ./ -type d | cut -c 3-12

que, presumivelmente, você recebe os timestamps no formato de época. Agora adicione um loop while:

find ./ -type d | cut -c 3-12 | while read datestamp
do
    printf %s "$datestamp"
    date -d "@$datestamp"
done

Note que em alguns shells, essa sintaxe obtém o loop while em um subshell, o que significa que se você tentar definir uma variável lá, ela não será visível depois que você deixar o loop. Para consertar isso, você precisa virar as coisas um pouco:

while read datestamp
do
    printf %s "$datestamp"
    date -d "@$datestamp"
done < <(find ./ -type d | cut -c 3-12)

que coloca o find no subshell e mantém o loop while no shell principal. Essa sintaxe (AT & T ksh , zsh e bash specific) é necessária apenas se você quiser reutilizar um resultado dentro do loop.

    
por 15.09.2015 / 11:17
6

Se você tem data GNU, pode converter datas lidas a partir de um arquivo de entrada. Você só precisa massagear um pouco os timestamps para que possa reconhecê-los. A sintaxe de entrada para um registro de data e hora com base na época do Unix é @ seguida pelo número de segundos, que pode conter um ponto decimal.

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

Eu faria isso de forma adequada - alimente uma lista de timestamps:

#!/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

Esta saída:

1442039711 Sat Sep 12 07:35:11 2015
1442134211 Sun Sep 13 09:50:11 2015
1442212521 Mon Sep 14 07:35:21 2015

Se você quiser um formato de saída específico, use strftime , por exemplo:

print $t->epoch, " ", $t->strftime("%Y-%m-%d %H:%M:%S"),"\n";

Qual transformar isso em um forro único no seu pipe:

 perl -MTime::Piece -nle '$t=Time::Piece->new($_); print $t->epoch, "  ", $t, "\n";'

Mas eu provavelmente sugeriria, em vez disso, usar o módulo File::Find e fazer a coisa toda no perl. Se você der um exemplo de sua estrutura de diretórios antes de cortá-la, eu darei um exemplo. Mas seria algo como:

#!/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, "." ); 
    
por 15.09.2015 / 11:14
2

Com zsh e o strftime incorporado:

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

assume que todos os nomes de diretório no diretório atual são, na verdade, épocas.
Mais filtragem / processamento é possível desde que você esclareça como esses números em seu exemplo devem ser processados (eles se parecem mais com épocas vezes correspondentes às datas de nascimento da Princesa Leia e Luke Skywalker ...), e. pesquisar recursivamente por nomes de diretório que correspondam a pelo menos 10 dígitos e calcular a data com base nos primeiros 10 dígitos:

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

Usando o GNU Parallel:

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

Se você pode aceitar \ t em vez de espaço:

find ./ -type d | cut -c 3-12 | parallel -k --tag date -d @{}
    
por 15.09.2015 / 16:01
0

Normalmente, o comando find pode ser encadeado com qualquer comando usando o argumento exec .

No seu caso, você pode fazer assim:

find . -type d | cut -c 3-12 | while read line
do
       echo -n "${line}  "
       date -d $line
done
    
por 15.09.2015 / 11:16
0

Usando o Python (é a solução possivelmente mais lenta)

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

dá:

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
    
por 16.09.2015 / 10:38

Tags