Rekursives Berühren der zuletzt geänderten Datumszeit der Verzeichnisse als die gleiche Datumszeit der zuletzt geänderten Datei

1368
Phil

Bisher habe ich mit dos2unixjedem *.phpBefehl rekursiv jede Datei auf meinem Server ausgeführt:

find . -name '*.php' -type f -exec dos2unix --keepdate {} + 

Alle Dateien, die von verarbeitet wurden, dos2unixhatten ihre letzte geänderte Datumszeit intakt, aber das übergeordnete Verzeichnis, das solche Dateien enthält, wurde immer noch auf die Zeit aktualisiert, zu der ich den Befehl ausführte.

Wie kann ich mit einem Einzeiler die Verzeichnisse rekursiv berühren und die zuletzt geänderte Datumszeit der Verzeichnisse auf dieselbe Datumszeit wie die zuletzt geänderte Datei im Verzeichnis setzen?

Davon:

dir_a/ 2013-05-13 14:05 abc.php 2012-09-01 12:34 def.php 2012-09-15 23:45 dir_b/ 2013-05-13 14:05 uvw.php 2012-10-01 01:23 xyz.php 2012-10-08 09:10 

Zu diesem:

dir_a/ 2012-09-15 23:45 abc.php 2012-09-01 12:34 def.php 2012-09-15 23:45 dir_b/ 2012-10-08 09:10 uvw.php 2012-10-01 01:23 xyz.php 2012-10-08 09:10 

Der Server läuft unter CentOS 5.6.

1

2 Antworten auf die Frage

2
Nykakin

Vielleicht so etwas:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done 

Erläuterung:

Um das Änderungsdatum von einer Datei an eine andere zu übergeben, müssen Sie Folgendes ausführen:

touch file1 -mr file2 

Zuerst müssen wir Unterverzeichnisse finden:

find . -type d -print0 | while read -r -d '' dir; do echo "$dir"; done 

In diesem Fall -execwürde die Option zu komplex werden. Sie müssen sicherstellen, dass Sie die Ausgabe von findmit NULals Datensatztrennzeichen übermitteln und angeben read, dass das Split ( -d '') erfolgen soll.

Um Dateien mit dem letzten Änderungsdatum zu finden, kann ich Folgendes verwenden:

find ./ -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2- 

Kombiniere es:

find . -mindepth 1 -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";echo "$dir <- $file"; done 

Zum Schluss fügen Sie einen Touch hinzu:

find . -mindepth 1 -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";touch "$dir" -mr "$file"; done 

Ich verwende die -mindepthOption hier, weil ich diesen Befehl aus einem Verzeichnis mit dir_a gestartet habe, das keine Datei zum Lesen des Datums enthält. Um dieses Problem zu beseitigen, kann ich if-else verwenden, um sicherzustellen, dass ich nicht versuche, die Zeit aus einer nicht vorhandenen Datei zu lesen:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done 
Vielen Dank für die Antwort, das hat fast perfekt funktioniert. Erst wenn "touch" versucht, die letzte geänderte Datumszeit aus einer Datei mit Leerzeichen im Dateinamen abzurufen, schlägt dies fehl. Phil vor 10 Jahren 0
@Phil Ja, das liegt wahrscheinlich an dem (gebrochenen) $ (find ...) `Konstrukt in der Mitte. Tauschen Sie das `-f2` gegen -f2-` aus und umgeben Sie das `$ ()` mit doppelten Anführungszeichen. slhck vor 10 Jahren 2
@slhck Das Problem mit "touch" wurde behoben, aber jetzt habe ich festgestellt, dass "find" fehlschlägt, wenn ein Verzeichnisname Leerzeichen enthält. Kannst du darauf hinweisen, wie dies ebenfalls behoben werden kann? Vielen Dank. Phil vor 10 Jahren 0
@Phil Stimmt, dass `find… | while` war auch kaputt. Sie müssen es mit `-print0 | aufrufen lesen Sie dabei… -d '' `und zitieren Sie das '$ dir' in der inneren Schleife. slhck vor 10 Jahren 1
1
terdon

Das sollte es tun:

find -mindepth 1 -type d -print0 | while IFS= read -r -d '' dir; do  time=$( find "$dir" -mindepth 1 -maxdepth 1 -type f -exec stat -c %Y {} \; |  sort -nk2 | tail -n 1 ); touch -d @$time "$dir";  done 

ERLÄUTERUNG:

  • find -mindepth 1 -type d | while IFS= read -r -d '' dir; do: Durchläuft alle Verzeichnisse außer dem aktuellen Verzeichnis ( -mindepth 1). Durch Verwenden einer whileSchleife mit IFS=und das -rFlag wird sichergestellt, dass der Befehl mit Verzeichnisnamen funktioniert, die Leerzeichen oder neue Zeilen ( \n) oder andere merkwürdige Zeichen enthalten. Das -d ''Trennzeichen wird auf das Nullzeichen gesetzt, damit es mit der -print0Option find arbeiten kann .

  • find $dir -mindepth 1 -maxdepth 1 -type f -exec stat --printf="%Y\n" {} \;: Finden Sie alle Dateien in jedem Verzeichnis $dirund führen Sie sie aus, um statdas Änderungsdatum in Sekunden nach der Epoche auszugeben .

  • sort -nk2 | tail -n 1 : Dies sortiert nur die Daten und hält die neuesten Informationen. Die letzten beiden Teile zusammen geben die Änderungszeit der neuesten Datei an, die in der Variablen gespeichert wird $time.

  • touch -d @$time $dir: Hier verwenden wir den touchBefehl, um die Änderungszeit für jedes Verzeichnis festzulegen. Das @ist da, um zu sagen, touchdass wir seit der Epoche eine Zeit in Sekunden angeben, dies ist eine Standardfunktion von GNU coreutils .

Vielen Dank für die Antwort, aber ich bekomme eine Menge "touch: ungültiges Datumsformat \" @ "` - Fehler, wenn ich das ausführen. Phil vor 10 Jahren 0
@Phil Überprüfe, ob $ time etwas enthält, nachdem `$ (find…)` ausgeführt wurde. Sie sollten diese Fehlermeldung nur erhalten, wenn "$ time" leer ist. slhck vor 10 Jahren 0
@Phil das passiert, wenn Sie Verzeichnisse haben, die keine Dateien enthalten. Da der Wert von $ time von dem neuesten _file_ in jedem Verzeichnis abhängt, ist er leer, wenn die Verzeichnisse keine Dateien enthalten. Dies sollte jedoch kein Problem sein, ignorieren Sie einfach diese Meldungen, Sie kümmern sich nicht um Verzeichnisse ohne Dateien. terdon vor 10 Jahren 0