Wie kann ich eine Operation "Kopieren bei Änderung" ausführen?


34

Ich möchte eine Reihe von Dateien von Verzeichnis A in Verzeichnis B kopieren, mit dem Vorbehalt, dass, wenn eine Datei in Verzeichnis A mit einer Datei in Verzeichnis B identisch ist, diese Datei nicht kopiert werden sollte (und daher ihre Änderungszeit nicht sein sollte) aktualisiert). Gibt es eine Möglichkeit, dies mit vorhandenen Tools zu tun, ohne dafür ein eigenes Skript zu schreiben?

Um ein wenig auf meinen Anwendungsfall einzugehen: Ich .cerstelle automatisch eine Reihe von Dateien in einem temporären Verzeichnis (mit einer Methode, die alle Dateien bedingungslos generieren muss), und wenn ich sie erneut generiere, möchte ich nur kopieren Diejenigen, die sich in das eigentliche Quellverzeichnis geändert haben, lassen die unveränderten (mit ihren alten Erstellungszeiten) unberührt, makedamit Sie wissen, dass sie nicht neu kompiliert werden müssen. (Da es sich bei nicht allen generierten Dateien um .cDateien handelt, muss ich eher binäre Vergleiche als Textvergleiche durchführen.)

(Als Hinweis: Dies ergab sich aus der Frage, die ich unter https://stackoverflow.com/questions/8981552/speeding-up-file-comparions-with-cmp-on-cygwin/8981762#8981762 gestellt hatte , wo ich es versuchte Um die Skriptdatei zu beschleunigen, die ich für diese Operation verwendet habe, sollte ich mich wirklich fragen, ob es einen besseren Weg gibt, als mein eigenes Skript zu schreiben - zumal es eine einfache Möglichkeit gibt, dies in einer Shell zu tun Das Skript ruft so etwas wie cmpfür jedes Dateipaar auf, und das Starten all dieser Prozesse dauert zu lange.)


1
Sie können verwendet werden, diff -qr dirA dirBum zu sehen , welche Dateien sind einzigartig dirAund dirB, repectively.

1
@ brooks -moses das ist wirklich ein job für ccache geeignet !
ACULICH

3
@hesse, wenn Sie die eindeutigen Dateien anzeigen möchten, die Sie diff verwenden können, aber wenn Sie nur sehen möchten, was sich geändert hat, verwenden Sie rsync -avncoder den langen Weg rsync --archive --verbose --dry-run --checksum.
ACULICH

Antworten:


29

rsync ist wahrscheinlich das beste Tool dafür. Es gibt viele Optionen für diesen Befehl, lesen Sie die Manpage . Ich denke, Sie wollen die Option --checksum oder --ignore-times


Ich hätte feststellen müssen, dass ich das bereits ohne Erfolg versucht habe. Beide Optionen wirken sich nur darauf aus, ob rsync eine Kopie erstellt. Selbst wenn keine Kopie erstellt wird, wird entweder die Änderungszeit der Zieldatei auf die gleiche Zeit wie die der Quelle (sofern die -tOption angegeben ist) oder auf die Synchronisationszeit aktualisiert (falls -tnicht angegeben).
Brooks Moses

4
@ Brooks Moses: Das tut es nicht. Zumindest meine Version von rsyncnicht. Wenn ich dies tue mkdir src dest; echo a>src/a; rsync -c src/* dest; sleep 5; touch src/a; rsync -c src/* dest, dann stat dest/azeigt es, dass mtime und ctime 5 Sekunden älter sind als diejenigen von src/a.
Angus

@angus: Huh. Okay, du hast Recht. Der Schlüssel scheint die --checksumOption zu sein, und obwohl linux.die.net/man/1/rsync absolut nichts enthält , was darauf schließen lässt, dass es einen Einfluss darauf hat, ob das Änderungsdatum aktualisiert wird, bewirkt es dennoch, dass das Änderungsdatum des Ziels übrig bleibt unberührt. (Auf der anderen Seite hat die --ignore-timesOption diesen Effekt nicht. Damit wird das Änderungsdatum immer noch aktualisiert.) Kann ich mich jedoch darauf verlassen, da dies völlig undokumentiert zu sein scheint?
Brooks Moses

2
@BrooksMoses: Ich denke, Sie können sich darauf verlassen: rsync's Workflow ist: 1) Überprüfen Sie, ob die Datei aktualisiert werden muss; 2) Aktualisieren Sie in diesem Fall die Datei. Die --checksumOption sollte nicht aktualisiert werden und daher rsyncnicht mit Schritt 2 fortfahren.
Enzotib

2
@BrooksMoses: --ignore-timesOhne --checksumwürde jede Datei kopiert und so auch der Zeitstempel aktualisiert, auch wenn die Dateien identisch sind.
Enzotib

13

Sie können den -uSchalter verwenden, um Folgendes zu cpmögen:

$ cp -u [source] [destination]

Von der Manpage:

   -u, --update
       copy only when the SOURCE file is newer than the destination file or 
       when the destination file is missing

4
Hallo und willkommen auf der Seite. Wir erwarten, dass die Antworten hier etwas umfangreicher sind. Sie hätten zum Beispiel eine Erklärung hinzufügen können, was das -uFlag macht und wie es funktioniert und wie dies dem OP helfen würde. In diesem speziellen Fall würde es dem OP jedoch nicht helfen, da identische Dateien kopiert würden, wenn sie neuer wären, und so ihre Zeitstempel geändert würden, was genau das ist, was das OP vermeiden möchte.
Terdon

1
Aus einem Kommentar zu einem ähnlichen A, der bereits gelöscht wurde: "Dies funktioniert nicht, da es auch identische Dateien kopiert, wenn der Zeitstempel der Quelle neuer ist (und daher den Zeitstempel des Ziels gegen die OP-Anforderung aktualisiert)."
SLM

Beantwortet die Frage überhaupt nicht, aber ich fand sie trotzdem nützlich.
user31389

7

Während die Verwendung rsync --checksumeine gute allgemeine Möglichkeit zum "Kopieren bei Änderung" ist, gibt es in Ihrem speziellen Fall eine noch bessere Lösung!

Wenn Sie vermeiden möchten, dass Dateien unnötig neu kompiliert werden, sollten Sie den Ccache verwenden, der genau für diesen Zweck erstellt wurde! Tatsächlich werden dadurch nicht nur unnötige Neukompilierungen Ihrer automatisch generierten Dateien vermieden, sondern es werden auch die Dinge beschleunigt, wann immer Sie dies tun make cleanund von Grund auf neu kompilieren.

Als nächstes werden Sie sicher fragen: "Ist es sicher?" Nun, ja, wie die Website feststellt:

Ist es sicher?

Ja. Der wichtigste Aspekt eines Compiler-Cache ist es, immer genau die gleiche Ausgabe zu erzeugen, die der echte Compiler erzeugen würde. Dazu gehört, dass Sie genau dieselben Objektdateien und genau dieselben Compiler-Warnungen bereitstellen, die bei Verwendung des echten Compilers auftreten würden. Die einzige Möglichkeit, mit der Sie feststellen können, dass Sie Ccache verwenden, ist die Geschwindigkeit.

Und es ist einfach zu verwenden , indem Sie es einfach als Präfix in die CC=Zeile Ihres Makefiles einfügen (oder Sie können Symlinks verwenden, aber der Makefile-Weg ist wahrscheinlich besser).


1
Ich habe anfangs falsch verstanden und dachte, Sie schlagen vor, dass Sie einen Teil der Generierung mit Ccache durchführen, aber jetzt verstehe ich - Ihr Vorschlag war, dass ich einfach alle Dateien kopiere und dann Ccache im Erstellungsprozess verwende, um zu vermeiden, dass diejenigen, die Ccache verwenden, neu erstellt werden hatte sich nicht verändert. Es ist eine gute Idee, aber in meinem Fall nicht gut - ich habe Hunderte von Dateien, die normalerweise nur ein oder zwei auf einmal ändern, und laufe unter Cygwin, wo einfach die Hunderte von Ccache-Prozessen gestartet werden, um sie zu betrachten Datei würde einige Minuten dauern. Nichtsdestotrotz, weil es eine gute Antwort für die meisten Menschen ist!
Brooks Moses

Nein, ich habe nicht vorgeschlagen, dass Sie alle Dateien kopieren, sondern Sie können Ihre .c-Dateien einfach vor Ort automatisch generieren (entfernen Sie den Kopierschritt und schreiben Sie direkt darauf). Und dann benutze einfach ccache. Ich weiß nicht, was Sie damit meinen, dass Sie Hunderte von Ccache-Prozessen starten. Es handelt sich nur um einen leichten Wrapper um gcc, der ziemlich schnell ist und auch die Neuerstellung anderer Teile Ihres Projekts beschleunigt. Hast du es versucht? Ich möchte einen Vergleich des Timings zwischen der Verwendung Ihrer Kopiermethode und ccache sehen. Sie können beide Methoden kombinieren, um die Vorteile beider Methoden zu nutzen.
Aculich

1
Richtig, ok, ich verstehe jetzt etwas über das Kopieren. Um zu verdeutlichen, was ich damit meine, ist Folgendes: Wenn ich die Dateien an Ort und Stelle generiere, muss ich sie dann ccache file.c -o file.omehrere hundert Mal aufrufen , weil es mehrere hundert file.cDateien gibt. Wenn ich tat , dass mit cmp, anstatt ccache, dauerte es mehrere Minuten - und cmpist so leicht wie ccache. Das Problem ist, dass das Starten eines Prozesses unter Cygwin nicht unerhebliche Zeit in Anspruch nimmt, selbst für einen völlig trivialen Prozess.
Brooks Moses

1
Dauert als Datenpunkt for f in src/*; do /bin/true.exe; done30 Sekunden. Auf jeden Fall bevorzuge ich meinen Windows-basierten Editor und abgesehen von dieser Art von Zeitproblemen funktioniert Cygwin sehr gut mit meinem Workflow als einfachem Ort, um Dinge lokal zu testen, wenn ich sie nicht auf die Build-Server hochlade. Es ist nützlich, meine Shell und meinen Editor im selben Betriebssystem zu haben. :)
Brooks Moses

1
Wenn Sie Ihren Windows-basierten Editor verwenden möchten, können Sie dies mit Shared Folders ganz einfach tun, wenn Sie Guest Additions installieren ... aber hey, wenn Cygwin zu Ihnen passt, wer soll das anders sagen? Es scheint nur eine Schande zu sein, durch komische Reifen wie diesen springen zu müssen ... und das Kompilieren im Allgemeinen wäre auch in einer VM schneller.
ACULICH

3

Dies sollte tun, was Sie brauchen

diff -qr ./x ./y | awk '{print $2}' | xargs -n1 -J% cp % ./y/

Woher:

  • x ist Ihr aktualisierter / neuer Ordner
  • y ist das Ziel, an das Sie kopieren möchten
  • awk übernimmt das zweite Argument der einzelnen Zeilen aus dem Befehl diff (möglicherweise benötigen Sie zusätzliche Informationen für Dateinamen mit Leerzeichen - kann es jetzt nicht versuchen).
  • xargs -J% fügt den Dateinamen an der richtigen Stelle in cp ein

1
-1 weil dies zu kompliziert ist, nicht portierbar ( -Jist BSD-spezifisch; mit GNU XARGs ist es das -I) und nicht richtig funktioniert, wenn nicht an beiden Orten bereits derselbe Satz von Dateien existiert (wenn ich touch x/boodann grep gibt) Only in ./x: boowas zu Fehlern in der Pipeline führt). Verwenden Sie beispielsweise ein für diesen Job entwickeltes Tool rsync --checksum.
ACULICH

Oder noch besser, verwenden Sie für diesen speziellen Fall ccache .
ACULICH

+1 Da es sich um eine Reihe bekannter Befehle handelt, die ich
unterbrechen

3

Ich mag verwenden unisono für , rsyncweil es mehrere Master unterstützt, nachdem er bereits mein Setup SSH - Schlüssel und vpn getrennt.

In meiner Crontab von nur einem Host habe ich sie alle 15 Minuten synchronisieren lassen:

* / 15 * * * * [-z "$ (pidof unison)"] && (Zeitüberschreitung 25m unison -sortbysize -ui text -batch -times / home / master ssh: //192.168.1.12//home/master -path dev -logfile /tmp/sync.master.dev.log) &> /tmp/sync.master.dev.log

Dann kann ich mich auf beiden Seiten entwickeln und die Veränderungen werden sich verbreiten. Tatsächlich habe ich für wichtige Projekte bis zu 4 Server, die den gleichen Baum spiegeln (3 laufen unisono von cron und zeigen auf denjenigen, der dies nicht tut). Tatsächlich sind Linux- und Cygwin-Hosts gemischt - außer Sie erwarten keinen Sinn von Softlinks in win32 außerhalb der Cygwin-Umgebung.

Wenn Sie diesen Weg gehen, machen Sie den ersten Spiegel auf der leeren Seite ohne die -batch, dh

unison -ui text  -times /home/master ssh://192.168.1.12//home/master -path dev

Natürlich gibt es eine Konfiguration zum Ignorieren von Backup-Dateien, Archiven usw .:

 ~/.unison/default.prf :
# Unison preferences file
ignore = Name {,.}*{.sh~}
ignore = Name {,.}*{.rb~}
ignore = Name {,.}*{.bak}
ignore = Name {,.}*{.tmp}
ignore = Name {,.}*{.txt~}
ignore = Name {,.}*{.pl~}
ignore = Name {.unison.}*
ignore = Name {,.}*{.zip}

    # Use this command for displaying diffs
    diff = diff -y -W 79 --suppress-common-lines

    ignore = Name *~
    ignore = Name .*~
    ignore = Path */pilot/backup/Archive_*
    ignore = Name *.o

Ich habe mir das angeschaut, aber ich konnte keine unisonOption finden, die bedeutet, dass das Datum der letzten Änderung der Datei nicht aktualisiert wird. Ist dort eines? Ansonsten ist dies eine großartige Antwort auf ein ganz anderes Problem.
Brooks Moses

1
-timestut das für mich. Unison hat auch einen Trockenlaufmodus, denkt ich.
Marcos

Nun, setzen times=false(oder aufhören -times) würde das tun. Ich weiß nicht, wie ich das in der Dokumentation verpasst habe. Vielen Dank!
Brooks Moses

Froh, dass ich Helfen kann. Ich bin ein Kniffel, wenn es darum geht, Dinge wie Modtimes, Berechtigungen und Softlinks zu bewahren. Oft übersehen
Marcos

1

Während rsync --checksumist die richtige Antwort, beachten Sie, dass diese Option mit nicht kompatibel ist --times, und das --archivebeinhaltet --times, so dass , wenn Sie möchten rsync -a --checksum, die Sie wirklich brauchen rsync -a --no-times --checksum.


Was meinst du mit "inkompatibel"?
ov

Was meinst du mit "ist die richtige Antwort"?
Thoni56
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.