Znajdź pliki i tar je (ze spacjami)

110

W porządku, taki prosty problem. Pracuję nad prostym kodem zapasowym. Działa dobrze, chyba że pliki zawierają spacje. Oto jak znajduję pliki i dodam je do archiwum tar:

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

Problem występuje, gdy plik ma spację w nazwie, ponieważ tar myśli, że jest to folder. Zasadniczo czy istnieje sposób, w jaki mogę dodawać cudzysłowy wokół wyników wyszukiwania? Albo inny sposób, aby to naprawić?

Caleb Kester
źródło
12
Najlepszym sposobem wykorzystania find ... | xargs ...jest użycie -print0 / -0 parametru na każdy: find -print0 ... | xargs -0 .... Spowoduje to, że nazwy plików zostaną oddzielone znakiem null, co oznacza, że ​​możesz mieć spacje, znaki nowej linii lub inne dziwne rzeczy w nazwach plików i nadal będzie działać.
porge
8
Występuje problem z używaniem xargs i tar w ten sposób, gdy masz dużą liczbę plików, xargs będzie wielokrotnie wywoływał tar -c, co będzie nadpisywać twoje archiwum, w wyniku czego nie będziesz mieć wszystkich plików, których oczekujesz . Zobacz to bardziej szczegółowe wyjaśnienie i moją odpowiedź poniżej.
Steve Kehlet

Odpowiedzi:

217

Użyj tego:

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

To będzie:

  • radzą sobie z plikami zawierającymi spacje, znaki nowej linii, początkowe myślniki i inne dziwactwa
  • obsłużyć nieograniczoną liczbę plików
  • nie będzie wielokrotnie nadpisywać kopii zapasowej.tar.gz, tak jak tar -cw xargsprzypadku, gdy masz dużą liczbę plików

Zobacz także:

Steve Kehlet
źródło
1
jak byś to zrobił, gdybyś chciał najpierw przepuścić swoje znalezisko przez sed kilka razy? np. znajdź. -print0 | sed / backups / d | tar ....
Brad Parks
8
Zauważ, że jeśli masz wiele warunków, musisz dodać nawiasy. W przeciwnym razie -print0ma zastosowanie tylko do ostatniego wyrażenia. Np.find . \( -type f -o -name '*.c' \) -print0 | ...
nimrodm
1
Dla zabawy, oto wersja dla Windowsa używająca 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, czy możesz wyjaśnić, co to jest opcja „-” na końcu polecenia tar. Nie mogę go znaleźć na stronie podręcznika GNU tar.
shaffooo
Jasne, jest to parametr do -T, a to oznacza odczyt nazw plików ze standardowego wejścia: Jeśli podasz pojedynczy myślnik jako nazwę pliku dla `--files-from ', (tj. Określisz albo --files-from = - lub -T -), wtedy nazwy plików są odczytywane ze standardowego wejścia
Steve Kehlet
14

Może istnieć inny sposób na osiągnięcie tego, czego chcesz. Gruntownie,

  1. Użyj polecenia find, aby wyświetlić ścieżkę do dowolnych plików, których szukasz. Przekieruj standardowe wyjście do wybranej nazwy pliku.
  2. Następnie tar z opcją -T, która pozwala na pobranie listy lokalizacji plików (tej, którą właśnie utworzyłeś za pomocą find!)

    find . -name "*.whatever" > yourListOfFiles
    tar -cvf yourfile.tar -T yourListOfFiles
    
podatne na błędy
źródło
Jest tu na odpowiedź, jak radzić sobie z nowymi liniami nazwy plików w nich superuser.com/a/513319/151261
tommy.carstensen
8

Spróbuj biegać:

    find . -type f | xargs -d "\n" tar -czvf backup.tar.gz 
gsteff
źródło
7

Dlaczego nie:

tar czvf backup.tar.gz *

Jasne, że sprytnie jest użyć funkcji find, a następnie xargs, ale robisz to na własnej skórze.

Aktualizacja: Porges skomentował opcję znajdowania, która moim zdaniem jest lepszą odpowiedzią niż moja odpowiedź lub inna: find -print0 ... | xargs -0 ....

Warren P
źródło
Mój pełny kod utworzy kopie zapasowe tylko elementów zmodyfikowanych w minionym dniu. Ponieważ jest to codzienna kopia zapasowa, nie chcę powtarzać informacji w celu zapisania rozmiaru pliku (mam również pełną kopię zapasową co 15 dni).
Caleb Kester
Aby uczynić to lepszym pytaniem SO, zadałbym pytanie o „niezawodne używanie razem funkcji find, xargs i tar”. Twój tytuł i pytanie tak naprawdę nie określają, że potrzebujesz znaleźć i xargs, a jednak tak jest.
Warren P
xargs ... tar c ...nadpisze pierwsze utworzone archiwum, jeśli lista plików jest zbyt długa i xargsuruchomi się tarpo raz drugi! Aby uniknąć nadpisywania, możesz użyć, xargs -xale wtedy archiwum może być niekompletne. Alternatywą może być najpierw, tar c ...a potem prawdopodobnie wielokrotnie tar r .... (mój wkład w niezawodność :)
pabuk
3

Jeśli masz wiele plików lub katalogów i chcesz je spakować do niezależnego *.gzpliku, możesz to zrobić. Opcjonalny-type f -atime

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

To się skompresuje

httpd-log01.txt
httpd-log02.txt

do

httpd-log01.txt.gz
httpd-log02.txt.gz
Kalibur x
źródło
2

Dlaczego nie spróbować czegoś takiego: tar cvf scala.tar `find src -name *.scala`

Frank Eggink
źródło
2

Inne rozwiązanie, jak widać tutaj :

find var/log/ -iname "anaconda.*" -exec tar -cvzf file.tar.gz {} +
tommy.carstensen
źródło
2

Dodałby komentarz do posta @Steve Kehlet, ale potrzebuję 50 powtórzeń (RIP).

Dla każdego, kto znalazł ten post poprzez liczne wyszukiwania w Google, znalazłem sposób, aby nie tylko znaleźć określone pliki w określonym przedziale czasowym, ale także NIE uwzględniać ścieżek względnych LUB białych znaków, które powodowałyby błędy tarowania. (DZIĘKUJĘ WIELE STEVE).

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

  2. -name "*.pdf" poszukaj plików PDF (lub dowolnego typu)

  3. -type f typ do wyszukania to plik

  4. -mtime 0 szukaj plików utworzonych w ciągu ostatnich 24 godzin

  5. -printf "%f\0"Zwykłe -print0LUB -printf "%f"NIE działa dla mnie. Ze stron podręcznika:

To cytowanie jest wykonywane w taki sam sposób jak dla GNU ls. To nie jest ten sam mechanizm cytowania, jak ten używany dla -ls i -fls. Jeśli jesteś w stanie zdecydować, jakiego formatu użyć do wyjścia find, zwykle lepiej jest użyć „\ 0” jako terminatora niż użyć znaku nowej linii, ponieważ nazwy plików mogą zawierać znaki odstępu i znaku nowej linii.

  1. -czvf tworzenie archiwum, filtrowanie archiwum przez gzip, szczegółowa lista przetworzonych plików, nazwa archiwum

Edycja 2019-08-14: Chciałbym dodać, że mogłem również użyć zasadniczo tego samego polecenia w moim komentarzu, używając tylko samego tar:

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

Potrzebne --ignore-failed-readna wypadek, gdyby nie było dzisiaj nowych plików PDF.

user3472383
źródło
1

Najlepszym rozwiązaniem wydaje się utworzenie listy plików, a następnie zarchiwizowanie plików, ponieważ można użyć innych źródeł i zrobić coś innego z listą.

Na przykład pozwala to na użycie listy do obliczenia rozmiaru archiwizowanych plików:

#!/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
źródło
Jedna wkładka do tego?
Robino