So kombinieren Sie den Befehl 'tar' mit 'find'

31

Der Befehl find gibt Folgendes aus:

[root @ localhost /] # find var / log / -iname anaconda. *
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Nach der Kombination mit tar wird folgende Ausgabe angezeigt:

[root @ localhost /] # find var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Beim Auflisten einer TAR-Datei wird jedoch nur eine einzige Datei angezeigt

[root @ localhost /] # tar -tvf file.tar
-rw ------- root / root 208454 27.02.2012 12:01 var / log / anaconda.storage.log

Was mache ich hier falsch?

Mit xargs erhalte ich diese Ausgabe:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf file1.tar

Zweite Frage

Wenn Sie / vor var eingeben , bedeutet dies, find /var/logwarum es diesen Mesaage- Teer ausgibt : Führendes `/ 'aus den Mitgliedsnamen entfernen

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.log
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.xlog
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.yum.log
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.syslog
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.program.log
tar: Führendes `/ 'aus Mitgliedsnamen entfernen
/var/log/anaconda.storage.log

Was ist in einer einfachen Form der Unterschied zwischen den folgenden beiden?

find var/log und find /var/log

max
quelle
Dies ist ein Teil des Themas, aber in Zukunft findsollten Sie den Suchbegriff angeben. Es funktioniert ohne manchmal aber nicht immer.
Nerdwaller
1
Wenn Sie {} +stattdessen verwenden {} \;, werden die Ergebnisse von find in einem Argument zusammengefasst
Jason S

Antworten:

39

Hinweis: Siehe @ Iain Antwort für eine etwas effizientere Lösung.

Beachten Sie, dass finddie -execAktion für jede einzelne gefundene Datei aufgerufen wird .

Wenn Sie tar -cvf file.tar {}für jede einzelne Dateiausgabe ausführen find, bedeutet dies, dass Sie sie file.tarjedes Mal überschreiben. Dies erklärt, warum Sie nur noch ein Archiv haben, das nur noch enthält anaconda.storage.log- es ist die letzte Dateiausgabe find.

Jetzt möchten Sie die Dateien tatsächlich an das Archiv anhängen , anstatt sie jedes Mal zu erstellen (dies ist die -cOption). Verwenden Sie also Folgendes:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

Die -rOption wird an das Archiv angehängt, anstatt es jedes Mal neu zu erstellen.

Hinweis: Ersetzen Sie -iname anaconda.*durch -iname "anaconda.*". Das Sternchen ist ein Platzhalter und kann von Ihrer Shell erweitert werden, bevor es findüberhaupt angezeigt wird. Um diese Erweiterung zu verhindern, setzen Sie das Argument in doppelte Anführungszeichen.


Zum tarEntfernen von führenden /: Das Archiv sollte nur relative Dateinamen enthalten . Wenn Sie Dateien mit einem vorangestellten Punkt hinzugefügt haben /, werden diese als absolute Dateinamen gespeichert , was /var/…beispielsweise wörtlich auf Ihrem Computer bedeutet .

IIRC Dies ist lediglich eine Vorsichtsmaßnahme für tarandere Implementierungen als GNU, und es ist auf diese Weise sicherer, da Sie Ihre tatsächlichen Daten nicht überschreiben, /var/…wenn Sie das Archiv extrahieren, wenn es relative Dateinamen enthält.

slhck
quelle
6
Beachten Sie jedoch, dass, wenn Sie versuchen, tarauf diese Weise ein tatsächliches Bandarchiv zu erstellen, indem Sie jeweils eine Datei hinzufügen, das Band zurückspulen und dann jedes Mal das Ganze erneut lesen, um zum Ende zu gelangen, das Ganze lächerlich langsam wäre. Ihre Lösung ist nur geeignet, wenn Sie die tar-Datei auf die Festplatte schreiben.
Nicole Hamilton
2
Stimmt, aber ich denke, wir können diese Situation ignorieren;)
Slhck
@slhck * ist ein Platzhalter, der mit allen Möglichkeiten übereinstimmen sollte, oder? aber hier find /var/log/ -iname anaconda*nichts geben und find /var/log/ -iname anaconda.*die Ausgabe geben, warum?
max
Wenn ein Platzhalter verbraucht ist, wird er von nicht mehr gesehen find. Also , wenn Sie anaconda*, und im aktuellen Ordner gibt es etwas namens zum Beispiel anaconda5(für diese Wildcard), wird der Platzhalter erweitert und findwerden sehen , -iname anaconda5statt -iname anaconda*. Warum das erste nicht funktioniert und das zweite, hängt davon ab, welche Dateien sich in Ihrem aktuellen Verzeichnis befinden. @max
slhck
2
Sie können {} +anstelle von verwenden, {} \;damit die Ergebnisse der Suche in einem Argument zusammengefasst werden
Jason S
41

Sie können Folgendes verwenden:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

Das -print0und -Tarbeiten zusammen, um Dateinamen mit Leerzeichen, Zeilenumbrüchen usw. zuzulassen. Das letzte -weist tar an, die eingegebenen Dateinamen von stdin zu lesen.

Beachten Sie, dass diese Antwort-print0 am Ende Ihrer Aussage stehen muss . Andernfalls erhalten Sie wahrscheinlich mehr Dateien als erwartet.

Peter Mortensen
quelle
2
Sie haben die -nameOption weggelassen und die Lösung für tardas gesamte Verzeichnis erstellt. Wenn das , was Sie wollen, können Sie es tun leichter als tar -cvf file.tar var/logohne findüberhaupt.
Nicole Hamilton
2
+1 Das Weiterleiten der Liste an tarist eine gute Idee. Es ist definitiv die beste Lösung, wenn Sie erwarten, dass die Pfadnamen Leerzeichen enthalten. Ich würde es sogar als das technisch beste bezeichnen, da es sowohl zuverlässig als auch effizient ist. Es sind jedoch zusätzliche Spezialkenntnisse in beiden findund erforderlich tar. Ich bevorzuge die Befehlsersetzung so ziemlich nur, weil es ein allgemeineres Werkzeug ist: Lerne, wie man es einmal benutzt und benutze es dann überall. (Aber ich gebe zu, ich bin auf Windows mit einer Shell, wo es immer funktioniert.) Entschuldigung, wenn ich unhöflich schien.
Nicole Hamilton
2
Sie haben bereits Ihre +1 erhalten. Sei glücklich. :) Lange Befehlszeilen sind immer der Fluch der Prozesserstellung in jedem Betriebssystem. Ich erinnere mich, wie ich Anfang der 90er Jahre mit Mark Lucovsky bei Microsoft darüber gestritten habe, dass die Beschränkung der 32-KB-Unicode-Zeichen für NT zu gering war, und dass ich keine Ahnung hatte, wie viele Bytes es dauern würde, um Längen als Longs und nicht als Shorts überall im Kernel zu speichern . Seufzer. Die allgemeineren Falllösungen, wenn die Argumentliste zu lang ist, sind, mehr in der Shell zu tun (wenn möglich; in meiner ist es) oder zu verwenden xargs.
Nicole Hamilton
9
Wenn Sie die -print0Option find verwenden , benötigen Sie auch die --nullOption tar .
1.
2
Und es --no-unquotestellt sich heraus, dass dies ebenfalls erforderlich ist: Dateinamen, die Backslashes enthalten, würden ansonsten falsch behandelt. (Nein, das ist keine Hypothese - ich erstelle wirklich ein Tar-Archiv aus dem Code einer anderen Person, das einen Dateinamen mit Backslashes im Namen enthält. So habe ich es herausgefunden.)
hvd
12

Versuche dies:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

Sie versuchen , zu verwenden , findzu -exec tar. Bei der Funktionsweise der -execOption wird dieser Befehl jedoch für jede gefundene übereinstimmende Datei einmal ausgeführt, wodurch die tarvon ihr erzeugte TAR-Datei jedes Mal überschrieben wird. Deswegen bist du nur beim letzten gelandet. Außerdem müssen Sie das angegebene Muster in Anführungszeichen setzen, finddamit die Shell es nicht erweitert, bevor Sie es an übergeben find.

Wenn Sie die Befehlsersetzung mit Backticks verwenden (oder die $(...)Notation verwenden, wenn Sie dies vorziehen), wird die gesamte Liste der Namen, die von erzeugt wurden find, als Argumente in die Befehlszeile eingefügt tar, sodass alle auf einmal geschrieben werden.

Nicole Hamilton
quelle
2
Dies kann zu Problemen führen, wenn bei der Suche nach Ausgabedateien Leerzeichen im Namen, Zeilenumbrüche oder Zeilenumbrüche verwendet werden. Dies ist mit Sicherheit ein Misserfolg - die Ableitung von Standardwerten findist selten eine gute Idee. mywiki.wooledge.org/ParsingLs
slhck
3
@slhck, stdout von find zu leiten ist in der Regel eine gute Idee, wie auf der Seite, auf die Sie in Ihrem Kommentar verlinkt haben, sehr deutlich erklärt wird :). Es ist in der Tat die empfohlene Vorgehensweise. Sie sollten einfach ein paar Tricks (wie read -rvon -print0) verwenden, wie ich es in meiner Antwort getan habe.
Terdon
4
@slhck Aus diesem Grund haben Datei- und Verzeichnisnamen in Unix und Linux traditionell Leerzeichen in Namen vermieden. Das ist auch der Grund, warum ich unter Windows, wo Namen mit Leerzeichen häufig verwendet werden, meiner eigenen Hamilton C-Shell eine zusätzliche Notation für die Befehlsersetzung hinzugefügt habe. Dabei werden die gesamten Zeilen (möglicherweise einschließlich Leerzeichen) als einzelne Wörter behandelt, die wieder in den Befehl eingefügt werden Linie. Leider hat keine der Unix-Shells diese Funktion.
Nicole Hamilton
1
Sie haben es vielleicht traditionell vermieden, aber mit Dateien, die über GUIs im Benutzerbereich erstellt werden, können Sie Dateien mit Leerzeichen nicht mehr vernachlässigen und sie als Bürger zweiter Klasse behandeln (nur weil es Unix ist). Es ist schön, dass Sie das in Ihre Shell aufgenommen haben, aber es ist für Windows, und Unix-Shells benötigen diese Funktion nicht besonders , wenn Sie einfach die richtige Syntax verwenden und die richtigen Vorsichtsmaßnahmen treffen. Deshalb habe ich meinen Kommentar an erster Stelle gepostet.
Slhck
2
Nein, aber an anderen Orten könnte es sehr gut passieren. Deshalb ist es eine gute Idee, defensiv zu programmieren - lieber auf Nummer sicher als auf Nummer sicher. Besucher, die diese Frage finden, haben möglicherweise nicht unbedingt das gleiche Problem und fragen sich, warum der hier gefundene Befehl für diesen Fall geeignet zu sein scheint, für sie jedoch fehlgeschlagen ist. Ich überlasse es Ihnen, den Befehl zu korrigieren. Ich dachte nur, es sei wichtig, ihn zu erwähnen, weil viele Leute früher oder später auf dieses Problem stoßen.
Slhck
6

Frage 1

Ihr Befehl schlägt fehl, weil taralle gefundenen Dateien übernommen und archiviert werden file.tar. Jedes Mal, wenn dies der Fall ist, wird der zuvor erstellte Wert überschrieben file.tar.

Wenn Sie nur ein Archiv mit allen Dateien haben möchten, führen Sie es einfach tardirekt aus, es ist nicht erforderlich find(und ja, dies funktioniert für Dateien mit Leerzeichen im Namen):

tar -vcf file.tar /var/log/anaconda*   

Frage 2

Die beiden Befehle sind völlig unterschiedlich:

  • find var / log durchsucht ein Verzeichnis mit dem Namen, var/log das ein Unterverzeichnis Ihres aktuellen Verzeichnisses ist find ./var/log(beachten Sie das ./).

  • find / var / log durchsucht ein Verzeichnis mit dem Namen, /var/log das ein Unterverzeichnis des Stammverzeichnisses ist/ .

Die /Leitbotschaft kommt von tar, nicht find. Dies bedeutet, dass der erste /Ihrer Dateinamen entfernt wird, um absolute Pfade zu relativen Pfaden zu machen . Dies bedeutet, dass die Datei von /var/log/anaconda.errornach extrahiert wird, ./var/log/anaconda.errorwenn Sie das Archiv entpacken.

terdon
quelle
1

Es gibt zwei Möglichkeiten, -execdie funktionieren können. Auf eine Weise wird der Befehl mehrmals ausgeführt - einmal für jede Datei. Auf die andere Weise wird der Befehl einmal ausgeführt, einschließlich aller Dateien als Parameterliste.

  • -exec tar -cvf file.tar {} ';'Führt den tarBefehl für jede Datei aus und überschreibt das Archiv jedes Mal.
  • -exec tar -cvf file.tar {} '+'Führt den tarBefehl einmal aus und erstellt ein Archiv aller gefundenen Dateien.
mwfearnley
quelle
1

Ich denke, die Verwendung von -exec für jede Datei kann die Tar-Komprimierung sehr langsam machen, wenn Sie viele Dateien haben. Ich bevorzuge den Befehl:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
Fabceolin
quelle
bis es anfängt zu scheitern/bin/cpio: xxx: Cannot open: Too many open files
SYN