Wie verkette ich die Befehle 'date -d @xxxxxx' und 'find ./'?

14

Ich habe Verzeichnisse, deren Namen Zeitstempel sind, angegeben in Millisekunden seit dem 01.01.1970:

1439715011728
1439793321429
1439879712214
.
.

Und ich brauche eine Ausgabe wie:

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

Ich kann alle Verzeichnisse mit folgendem Befehl auflisten:

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

Aber ich kann die Ausgabe nicht auf den nächsten Befehl setzen: date -d @xxxxxxund die Ausgabe manipulieren.

Wie kann ich das machen?

BlaMa
quelle
2
Wie werden diese Zeitstempel in eine Epoche übersetzt? Weil deine Zahlen zu lang sind ... (Das erste - ist Fri Oct 2 05:35:28 47592)
Sobrique
1
@Sobrique Eindeutig Millisekunden seit der Epoche.
Gilles 'SO - hör auf böse zu sein'

Antworten:

10

Sie sind auf dem richtigen Weg (für eine einfachere Lösung, die nur 2 oder 3 Befehle ausführt, siehe unten). Sie sollten *statt verwenden ./, um das aktuelle Verzeichnis loszuwerden¹ und dies vereinfacht das Schneiden der Millisekunden etwas, dann leiten Sie das Ergebnis einfach in GNU paralleloder xargs²:

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

bekommen

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

und um den Sekundenversatz davor zu addieren, wie Ihr Beispiel zeigt:

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

oder:

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

bekommen:

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

Es ist jedoch einfacher zu tun³:

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

Dadurch erhalten Sie erneut die gleiche angeforderte Ausgabe.

Der Nachteil bei der Verwendung *ist, dass Sie für die Erweiterung auf Ihre Befehlszeile beschränkt sind. Der Vorteil ist jedoch, dass Sie Ihre Verzeichnisse nach dem Zeitstempelwert sortiert bekommen. Wenn die Anzahl der Verzeichnisse ein Problem ist, verwenden Sie -mindepth 1, aber verlieren Sie die Reihenfolge:

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

und sortbei Bedarf einfügen :

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

¹ Dies setzt voraus, dass keine verschachtelten Unterverzeichnisse vorhanden sind, wie dies in Ihrem Beispiel der Fall zu sein scheint. Sie können auch verwenden , ./ -mindepth 1statt*
² Sie können ersetzen parallelmit xargs -I{}hier als @hobbs und @don_crissti vorgeschlagen, es ist nur ausführlicher. ³ basierend auf der Antwort von Gilles zur Verwendung der dateDateilesefunktionen

Anthon
quelle
Oder xargswenn Sie nicht haben parallel, was viele Leute wahrscheinlich nicht tun.
Hobbs
@hobbs AFAIK xargshat nicht die Möglichkeit zu geben , wo das Argument wie geht parallelhat mit {}.
Anthon
4
Es macht:find ./ -type d | cut -c 3-12 | xargs -I{} date --d @{} +'%Y-%m-%d'
don_crissti
@Anthon tut es, wenn Sie die -IOption verwenden.
Hobbs
1
@Anthon, GNU-Long-Optionen können abgekürzt werden, solange sie nicht mehrdeutig sind. --doder --dawürde mit aktuellen Versionen von GNU datefunktionieren, könnte aber aufhören zu funktionieren. Der Tag dateführt eine --dalekOption ein (für Daten im Dalek-Kalender).
Stéphane Chazelas
10

Ich würde vermeiden, mehrere Befehle pro Datei in einer Schleife auszuführen. Da Sie bereits GNUisms verwenden:

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

Womit nur zwei Befehle ausgeführt werden. strftime()ist GNU-spezifisch, wie date -d.

Stéphane Chazelas
quelle
Dies verkürzt nicht die Millisekunden der Verzeichnisnamen, sondern zeigt die vollständigen 13 Zeichen anstelle der ersten 10 an
Anthon
@Anthon, ah ja, diese Anforderung verpasst. Sollte jetzt OK sein.
Stéphane Chazelas
8

Du hast bereits:

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

Damit erhalten Sie vermutlich die Zeitstempel im Epochenformat. Fügen Sie nun eine while-Schleife hinzu:

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

Beachten Sie jedoch, dass diese Syntax in einigen Shells die while-Schleife in einer Subshell abruft. Wenn Sie also versuchen, eine Variable dort festzulegen, wird sie nicht sichtbar, sobald Sie die Schleife verlassen haben. Um das zu beheben, müssen Sie die Dinge leicht auf den Kopf stellen:

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

Das versetzt die findin die Subshell und hält die while-Schleife in der Mainshell. Das Syntax (AT & T ksh, zshund bashspezifisch) ist nur erforderlich , wenn Sie ein Ergebnis aus dem Innern der Schleife wieder zu verwenden suchen, though.

Wouter Verhelst
quelle
Unabhängig davon, zu sagen, dass es bash-spezifisch ist, ist nicht korrekt :)
Wouter Verhelst
Tatsächlich, wie man es zunächst geschrieben hatte, done <(find)statt done < <(find), es war richtig für yash(wo <(...)ist die Prozess Umleitung, keine Prozess Substitution), so meine bearbeiten ein bisschen Kavalier war , wie es die Schale gewesen sein könnte, dass für gemeint.
Stéphane Chazelas
6

Wenn Sie GNU-Datum haben, kann es Datumsangaben konvertieren, die aus einer Eingabedatei gelesen wurden. Sie müssen nur die Zeitstempel ein wenig massieren, damit sie erkannt werden. Der Eingabesyntax für einen Zeitstempel, der auf der Unix-Epoche basiert, @folgt die Anzahl der Sekunden, die einen Dezimalpunkt enthalten können.

find ./ -type d ! -name '*[!0-9]*' |
sed -e 's~.*/~@~' -e 's~[0-9][0-9][0-9]$~.&~' |
date -f - +'%s  %c'
Gilles 'SO - hör auf böse zu sein'
quelle
+1 für die Verwendung date Lesen s-Dateien. Dies ergibt sich date: invalid date ‘@’aufgrund der Übersetzung des aktuellen Verzeichnisses ( ./). Und da Sie die Millisekunden wegwerfen können, können Sie die zweite sedBearbeitung vereinfachen, indem Sie nur die letzten 3 Zeichen ablegen. Oder entfernen Sie alles und verwenden Siefind * -type d -printf "@%.10f" | date ...
Anthon
5

Ich würde es perlish tun - füttere eine Liste von Zeitstempeln:

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

Dies gibt aus:

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

Wenn Sie ein bestimmtes Ausgabeformat wünschen, können Sie strftimezB verwenden:

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

Welche, um dies in einen Einzeiler in Ihrer Pfeife zu verwandeln:

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

Aber ich würde wahrscheinlich vorschlagen, stattdessen das File::FindModul zu verwenden und das Ganze in Perl zu machen. Wenn Sie vor dem Ausschneiden ein Beispiel für Ihre Verzeichnisstruktur angeben, gebe ich Ihnen ein Beispiel. Aber es wäre so etwas wie:

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

Mit zsh und in der Strftime eingebaut:

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

Dies setzt voraus, dass alle Ihre Verzeichnisnamen im aktuellen Verzeichnis tatsächlich Epochen sind.
Weiteres Filtern / Verarbeiten ist möglich, vorausgesetzt Sie legen fest, wie diese Zahlen in Ihrem Beispiel verarbeitet werden sollen (sie ähneln eher den Epochenzeiten, die den Geburtsdaten von Prinzessin Leia und Luke Skywalker entsprechen ...), z. B. suchen Sie rekursiv nach Verzeichnisnamen, die mindestens übereinstimmen 10 Stellen und berechnen Sie das Datum anhand der ersten 10 Stellen:

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

Verwenden von GNU Parallel:

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

Wenn Sie statt Leerzeichen \ t akzeptieren können:

find ./ -type d | cut -c 3-12 | parallel -k --tag date -d @{}
Ole Tange
quelle
Beachten Sie, dass parallelin geschrieben ist perl. Dies scheint übertrieben, wenn man bedenkt, dass perles einen strftime()Operator gibt. Likeperl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
Stéphane Chazelas
2
1. Es ist kürzer. 2. Sie müssen Perl nicht lernen.
Ole Tange,
1
Es ist 27% kürzer, aber mehrere Größenordnungen weniger effizient (ungefähr 800-mal so langsam in dem Test, den ich durchgeführt habe; bedenken Sie, dass eine Shell (Ihre Shell, nicht / bin / sh) und ein Datumsbefehl für jede Zeile erzeugt werden müssen). und unfreundlich für das System, da es alle CPUs auf einmal belastet. Und du musst noch lernen parallel. IMO parallelist ein großartiges Tool zur Parallelisierung von CPU-intensiven Aufgaben, das jedoch für diese Art von Aufgaben hier nicht geeignet ist.
Stéphane Chazelas
Es gibt viele Kontexte, in denen Effizienz keine Rolle spielt. Es ist also immer noch eine akzeptable Lösung, aber es lohnt sich immer noch, das Leistungsproblem zu erwähnen, insbesondere wenn man bedenkt, dass sich Parallelen in der Regel mit hoher Leistung in den Köpfen der Menschen reimen .
Stéphane Chazelas
0

Normalerweise kann der Befehl find mit jedem Befehl verknüpft werden, der verwendet exec Arguments .

In Ihrem Fall können Sie folgendermaßen vorgehen:

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

Verwenden von Python (es ist möglicherweise die langsamere Lösung)

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

gibt:

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
Lukaz
quelle
Warum nicht alles in Python? Anstatt ein paar Pfeifen zu verketten?
Sobrique,
Es würde besser Sinn machen. Genau.
Lukaz