Finde Dateien und tariere sie (mit Leerzeichen)

110

Okay, so einfaches Problem hier. Ich arbeite an einem einfachen Sicherungscode. Es funktioniert einwandfrei, außer wenn die Dateien Leerzeichen enthalten. So finde ich Dateien und füge sie einem Teerarchiv hinzu:

find . -type f | xargs tar -czvf backup.tar.gz 

Das Problem ist, wenn die Datei ein Leerzeichen im Namen enthält, weil tar denkt, dass es sich um einen Ordner handelt. Grundsätzlich gibt es eine Möglichkeit, Anführungszeichen um die Ergebnisse von find hinzuzufügen? Oder eine andere Möglichkeit, dies zu beheben?

Caleb Kester
quelle
12
Die beste Verwendung find ... | xargs ...ist die Verwendung des Parameters -print0 / -0 für jedes : find -print0 ... | xargs -0 .... Dies führt dazu, dass die Dateinamen durch ein Nullzeichen getrennt werden. Dies bedeutet, dass Ihre Dateinamen Leerzeichen oder Zeilenumbrüche oder andere seltsame Elemente enthalten können und dies weiterhin funktioniert.
porges
8
Es gibt ein Problem bei der Verwendung von xargs und tar auf diese Weise, wenn Sie eine große Anzahl von Dateien haben. Xargs ruft wiederholt tar -c auf, wodurch Ihr Archiv immer wieder überschrieben wird und Sie nicht alle erwarteten Dateien haben . Siehe diese ausführlichere Erklärung und meine Antwort unten.
Steve Kehlet

Antworten:

217

Benutze das:

find . -type f -print0 | tar -czvf backup.tar.gz --null -T -

Es wird:

  • Umgang mit Dateien mit Leerzeichen, Zeilenumbrüchen, führenden Bindestrichen und anderen lustigen Dingen
  • eine unbegrenzte Anzahl von Dateien verarbeiten
  • überschreibt Ihre backup.tar.gz nicht wiederholt wie tar -cbei xargs, wenn Sie eine große Anzahl von Dateien haben

Siehe auch:

Steve Kehlet
quelle
1
Wie würden Sie das tun, wenn Sie Ihren Fund zuerst ein paar Mal durch sed leiten wollten? zB finden. -print0 | sed / backups / d | Teer ....
Brad Parks
8
Beachten Sie, dass Sie bei mehreren Bedingungen Klammern hinzufügen müssen. Andernfalls -print0gilt das nur für den letzten Ausdruck. ZBfind . \( -type f -o -name '*.c' \) -print0 | ...
Nimrodm
1
Zum Spaß, hier ist eine Windows-Version davon mit Cygwin:c:\cygwin\bin\find . -regextype posix-egrep -regex '.*(sln^|vcxproj^|filters)$' -print0 | c:\cygwin\bin\tar -cvf MS_Projects.tar --null -T -
Jon
1
@Steve können Sie bitte erklären, was die Option '-' am Ende des Befehls tar ist. Ich kann es nicht in der Manpage von GNU tar finden.
Shaffooo
Sicher, es ist ein Parameter für -Tund es bedeutet, die Dateinamen von der Standardeingabe zu lesen: Wenn Sie einen einzelnen Bindestrich als Dateinamen für "--files-from" angeben (dh Sie geben entweder --files-from = an - oder -T -), dann werden die Dateinamen von der Standardeingabe gelesen
Steve Kehlet
14

Es könnte einen anderen Weg geben, um das zu erreichen, was Sie wollen. Grundsätzlich,

  1. Verwenden Sie den Befehl find, um den Pfad zu den gewünschten Dateien auszugeben. Leiten Sie stdout zu einem Dateinamen Ihrer Wahl um.
  2. Dann tar mit der Option -T, mit der eine Liste der Dateispeicherorte erstellt werden kann (die, die Sie gerade mit find erstellt haben!).

    find . -name "*.whatever" > yourListOfFiles
    tar -cvf yourfile.tar -T yourListOfFiles
    
fehleranfällig
quelle
Es gibt eine Antwort hier, wie Dateinamen mit Zeilenumbrüchen in ihnen zu behandeln: superuser.com/a/513319/151261
tommy.carstensen
8

Versuchen Sie zu laufen:

    find . -type f | xargs -d "\n" tar -czvf backup.tar.gz 
gsteff
quelle
7

Warum nicht:

tar czvf backup.tar.gz *

Sicher, es ist klug, find und dann xargs zu verwenden, aber du machst es auf die harte Tour.

Update: Porges hat mit einer Suchoption kommentiert, die meiner Meinung nach eine bessere Antwort ist als meine oder die andere: find -print0 ... | xargs -0 ....

Warren P.
quelle
Mein vollständiger Code sichert nur Elemente, die am letzten Tag geändert wurden. Da es sich um eine tägliche Sicherung handelt, möchte ich keine wiederholten Informationen zum Speichern der Dateigröße haben (ich habe auch alle 15 Tage eine vollständige Sicherung).
Caleb Kester
Um dies zu einer besseren SO-Frage zu machen, würde ich die Frage stellen, "find, xargs und tar zusammen zuverlässig zu verwenden". Ihr Titel und Ihre Frage geben nicht wirklich an, dass Sie find und xargs benötigen, und dennoch tun Sie dies.
Warren P
xargs ... tar c ...überschreibt das erste erstellte Archiv, wenn die Liste der Dateien zu lang ist, und xargswird tarzum zweiten Mal ausgeführt! Um ein Überschreiben zu vermeiden, können Sie verwenden, xargs -xaber dann könnte das Archiv unvollständig sein. Alternative könnte sein, zuerst tar c ...und dann möglicherweise wiederholt tar r .... (mein Beitrag zur Zuverlässigkeit :)
Pabouk
3

Wenn Sie mehrere Dateien oder Verzeichnisse haben und diese in eine unabhängige *.gzDatei komprimieren möchten, können Sie dies tun. Optional-type f -atime

find -name "httpd-log*.txt" -type f -mtime +1 -exec tar -vzcf {}.gz {} \;

Dies wird komprimiert

httpd-log01.txt
httpd-log02.txt

zu

httpd-log01.txt.gz
httpd-log02.txt.gz
Kalibur x
quelle
2

Probieren Sie so etwas aus: tar cvf scala.tar `find src -name *.scala`

Frank Eggink
quelle
2

Eine andere Lösung wie hier zu sehen :

find var/log/ -iname "anaconda.*" -exec tar -cvzf file.tar.gz {} +
tommy.carstensen
quelle
2

Würde @Steve Kehlet einen Kommentar hinzufügen , benötige aber 50 Wiederholungen (RIP).

Für alle, die diesen Beitrag durch zahlreiche Google-Vorgänge gefunden haben, habe ich eine Möglichkeit gefunden, nicht nur bestimmte Dateien in einem bestimmten Zeitraum zu finden, sondern auch NICHT die relativen Pfade ODER Leerzeichen einzuschließen, die Teerfehler verursachen würden. (Vielen Dank, Steve.)

find . -name "*.pdf" -type f -mtime 0 -printf "%f\0" | tar -czvf /dir/zip.tar.gz --null -T -
  1. . relatives Verzeichnis

  2. -name "*.pdf" Suchen Sie nach PDFs (oder einem beliebigen Dateityp).

  3. -type f Der zu suchende Typ ist eine Datei

  4. -mtime 0 Suchen Sie nach Dateien, die in den letzten 24 Stunden erstellt wurden

  5. -printf "%f\0"Normal -print0ODER -printf "%f"hat bei mir NICHT funktioniert. Aus Manpages:

Dieses Zitat wird auf die gleiche Weise wie für GNUs durchgeführt. Dies ist nicht der gleiche Anführungsmechanismus wie für -ls und -fls. Wenn Sie entscheiden können, welches Format für die Ausgabe von find verwendet werden soll, ist es normalerweise besser, '\ 0' als Abschlusszeichen zu verwenden, als Zeilenumbruch zu verwenden, da Dateinamen Leerzeichen und Zeilenumbruchzeichen enthalten können.

  1. -czvf Archiv erstellen, Archiv durch gzip filtern, verarbeitete Dateien ausführlich auflisten, Archivname

Edit 2019-08-14: Ich möchte hinzufügen, dass ich auch im Wesentlichen den gleichen Befehl in meinem Kommentar verwenden konnte, nur mit tar selbst:

tar -czvf /archiveDir/test.tar.gz --newer-mtime=0 --ignore-failed-read *.pdf

Wird benötigt, --ignore-failed-readfalls es für heute keine neuen PDFs gab.

user3472383
quelle
1

Die beste Lösung scheint darin zu bestehen, eine Dateiliste zu erstellen und dann Dateien zu archivieren, da Sie andere Quellen verwenden und mit der Liste etwas anderes tun können.

Dies ermöglicht beispielsweise die Verwendung der Liste zur Berechnung der Größe der zu archivierenden Dateien:

#!/bin/sh

backupFileName="backup-big-$(date +"%Y%m%d-%H%M")"
backupRoot="/var/www"
backupOutPath=""

archivePath=$backupOutPath$backupFileName.tar.gz
listOfFilesPath=$backupOutPath$backupFileName.filelist

#
# Make a list of files/directories to archive
#
echo "" > $listOfFilesPath
echo "${backupRoot}/uploads" >> $listOfFilesPath
echo "${backupRoot}/extra/user/data" >> $listOfFilesPath
find "${backupRoot}/drupal_root/sites/" -name "files" -type d >> $listOfFilesPath

#
# Size calculation
#
sizeForProgress=`
cat $listOfFilesPath | while read nextFile;do
    if [ ! -z "$nextFile" ]; then
        du -sb "$nextFile"
    fi
done | awk '{size+=$1} END {print size}'
`

#
# Archive with progress
#
## simple with dump of all files currently archived
#tar -czvf $archivePath -T $listOfFilesPath
## progress bar
sizeForShow=$(($sizeForProgress/1024/1024))
echo -e "\nRunning backup [source files are $sizeForShow MiB]\n"
tar -cPp -T $listOfFilesPath | pv -s $sizeForProgress | gzip > $archivePath
Nux
quelle
Ein Liner dafür?
Robino