Come concatenare i comandi 'date -d @xxxxxx' e 'find ./'?


14

Ho delle directory i cui nomi sono timestamp, dati in millisecondi dal 1970-01-01:

1439715011728
1439793321429
1439879712214
.
.

E ho bisogno di un output come:

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

Posso elencare tutte le directory per comando:

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

Ma non posso mettere l'output al comando successivo: date -d @xxxxxxe manipolare l'output.

Come posso fare questo?


2
In che modo questi timestamp si traducono in epoca? Perché i tuoi numeri sono troppo lunghi ... (Quella prima - è Fri Oct 2 05:35:28 47592)
Sobrique,

1
@Sobrique Chiaramente millisecondi dall'epoca.
Gilles 'SO- smetti di essere malvagio' il

Risposte:


10

Sei sulla buona strada (per una soluzione più semplice, eseguendo solo 2 o 3 comandi, vedi sotto). Dovresti usare *invece di ./sbarazzarti della directory corrente¹ e questo semplifica in qualche modo il taglio dei millisecondi, quindi inserisci semplicemente il risultato in GNU parallelo xargs²:

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

ottenere

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 per aggiungere l'offset dei secondi prima di quello indicato dall'esempio:

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

o:

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

ottenere:

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

Tuttavia è più semplice fare³:

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

che ti fornisce lo stesso output richiesto ancora una volta.

Lo svantaggio dell'uso *è che sei limitato dalla tua linea di comando per la sua espansione, il vantaggio tuttavia è che ottieni le tue directory ordinate per valore di data / ora. Se il numero di directory è un problema utilizzare -mindepth 1, ma si perde l'ordine:

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

e inserire sortse necessario:

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

¹ Questo presuppone che non ci siano sottodirectory nidificate, come sembra essere il tuo esempio. Puoi anche usare ./ -mindepth 1invece di*
² Puoi sostituirlo parallelcon xargs -I{}qui come suggerito da @hobbs e @don_crissti, è solo più dettagliato. ³ basato sulla risposta di Gilles per utilizzare datele capacità di lettura dei file


O xargsse non ce l'hai parallel, cosa che molte persone probabilmente non hanno.
Hobbs,

@hobbs per quanto ne so xargsnon ha la possibilità di specificare dove l'argomento va come parallelha con {}.
Anthon,

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

@Anthon lo fa se si utilizza l' -Iopzione.
Hobbs,

1
@Anthon, le opzioni lunghe GNU possono essere abbreviate purché non siano ambigue. --do --dafunzionerebbe con le versioni attuali di GNU date, ma potrebbe smettere di funzionare il giorno dateintroduce --dalekun'opzione (per le date nel calendario di Dalek).
Stéphane Chazelas,

10

Eviterei di eseguire diversi comandi per file in un ciclo. Dato che stai già usando GNUisms:

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

Che esegue solo due comandi. strftime()è specifico per GNU, come date -d.


Questo non riduce i millisecondi dei nomi delle directory ma mostra i 13 caratteri completi invece dei primi 10 richiesti
Anthon,

@Anthon, ah sì, ho perso questo requisito. Dovrebbe essere OK ora.
Stéphane Chazelas,

8

Hai già:

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

che presumibilmente ti dà i timestamp in formato epoca. Ora aggiungi un ciclo while:

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

Nota che in alcune shell, quella sintassi ottiene il ciclo while in una subshell, il che significa che se provi a impostare una variabile lì, non sarà visibile una volta che hai lasciato il ciclo. Per risolvere il problema, devi girare leggermente le cose sulla testa:

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

che inserisce il findsottotitolo e mantiene il ciclo while nella shell principale. Che la sintassi (AT & T ksh, zshe bashspecifico) è necessaria solo se si sta cercando di riutilizzare un risultato all'interno del ciclo, però.


a prescindere, dire che è specifico per bash non è corretto :)
Wouter Verhelst

In realtà, come avevi inizialmente scritto, done <(find)invece di done < <(find), era corretto yash(dove si <(...)trova il reindirizzamento del processo, non la sostituzione del processo), quindi la mia modifica era un po 'sprezzante in quanto avrebbe potuto essere la shell per cui intendevi.
Stéphane Chazelas,

6

Se hai una data GNU, può convertire le date lette da un file di input. Hai solo bisogno di massaggiare un po 'i timestamp in modo che possano riconoscerli. La sintassi di input per un timestamp basato sull'epoca Unix è @seguita dal numero di secondi, che può contenere un punto decimale.

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

+1 per l'utilizzo datedella lettura del file s. Questo darà una date: invalid date ‘@’causa della traduzione della directory corrente ( ./). E poiché puoi eliminare i millisecondi, puoi semplificare la seconda sedmodifica per eliminare solo gli ultimi 3 caratteri. Oppure rimuovi tutto e usafind * -type d -printf "@%.10f" | date ...
Anthon,

5

Lo farei perlishly - inserire un elenco di timestamp:

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

Questo produce:

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 si desidera un formato di output specifico, è possibile utilizzare strftimead esempio:

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

Che per trasformarlo in una fodera nella tua pipa:

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

Ma probabilmente suggerirei invece di usare il File::Findmodulo e fare invece tutto in perl. Se fai un esempio della tua struttura di directory prima di tagliarla, ti faccio un esempio. Ma sarebbe qualcosa del tipo:

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

Con zshe lo strftime builtin:

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

questo presuppone che tutti i nomi delle directory nella directory corrente siano in realtà epoche.
Ulteriori filtri / elaborazioni sono possibili a condizione che si chiarisca come devono essere elaborati quei numeri nel proprio esempio (sembrano più tempi epici corrispondenti alle date di nascita della Principessa Leia e Luke Skywalker ...) Ad esempio, cercare ricorsivamente nomi di directory che corrispondano almeno 10 cifre e calcola la data in base alle prime 10 cifre:

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

2

Usando GNU Parallel:

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

Se puoi accettare \ t invece di spazio:

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

Si noti che parallelè scritto in perl. Questo sembra eccessivo considerando che perlha un strftime()operatore. Mi piaceperl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
Stéphane Chazelas,

2
1. È più corto. 2. Non hai bisogno di imparare il Perl.
Ole Tange,

1
È più corto del 27%, ma è più efficiente di molti ordini di grandezza (circa 800 volte più lento nel test che ho fatto; considera che deve generare una shell (la tua shell, non / bin / sh) e un comando di data per ogni riga) e ostile al sistema in quanto carica tutte le CPU contemporaneamente. E hai ancora bisogno di imparare parallel. IMO, parallelè un ottimo strumento per parallelizzare le attività ad alta intensità di CPU, ma non è davvero appropriato per questo tipo di attività qui.
Stéphane Chazelas,

Ci sono molti contesti in cui l'efficienza non è un problema, quindi è ancora una soluzione accettabile, ma vale comunque la pena menzionare il problema delle prestazioni, soprattutto se si considera che il parallelo di solito fa rima con alte prestazioni nella mente delle persone.
Stéphane Chazelas,

0

Normalmente il comando find può essere concatenato con qualsiasi comando usando l' execargomento.

Nel tuo caso, puoi fare così:

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

0

Usare Python (è la soluzione più probabilmente più 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

Perché non fare tutto in Python? Piuttosto che concatenare un mucchio di tubi?
Sobrique,

Avrebbe senso. Sono d'accordo.
Lukaz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.