Знайдіть файли та націлюйте їх (із пробілами)


110

Гаразд, така проста проблема тут. Я працюю над простим резервним кодом. Це добре працює, за винятком випадків, коли у файлах є пробіли. Ось як я знаходжу файли та додаю їх до архіву tar:

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

Проблема полягає в тому, що у файлі є пробіл у імені, оскільки tar вважає, що це папка. В основному, чи є спосіб додавати цитати навколо результатів пошуку? Або інший спосіб це виправити?


12
Кращий спосіб використання find ... | xargs ...полягає у використанні -print0 / -0 параметра на кожному: find -print0 ... | xargs -0 .... Це призведе до того, що назви файлів будуть розділені нульовим символом, а це означає, що у ваших іменах можуть бути пробіли, нові рядки або інші дивні речі, і вони все одно працюватимуть.
porges

8
Проблема з використанням xargs та tar таким чином, коли у вас є велика кількість файлів, xargs буде неодноразово викликати tar -c, і це буде продовжувати перезаписувати ваш архів, і в результаті ви не будете мати всі файли, які ви очікуєте . Дивіться це докладніше пояснення та мою відповідь нижче.
Стів Келет

Відповіді:


217

Використовуй це:

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

Це буде:

  • обробляти файли з пробілами, новинками, провідними тире та ін
  • обробляти необмежену кількість файлів
  • не буде повторно перезаписати backup.tar.gz як використання tar -cз xargsробитиме , коли у вас є велика кількість файлів

Також дивіться:


1
як би ти це зробив, якби ти хотів спочатку кілька разів передати свою знахідку через sed? наприклад знайти. -принт0 | sed / резервні копії / д | tar ....
Бред Паркс

8
Зауважте, що якщо є кілька умов, вам потрібно додати дужки. Інакше -print0стосується лише останнього виразу. Напр.find . \( -type f -o -name '*.c' \) -print0 | ...
nimrodm

1
Для задоволення, ось версія Windows для цього, використовуючи 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, чи можете ви поясніть, що таке опція '-' в кінці команди tar. Я не можу його знайти на сторінці людини GNU tar.
shaffooo

Звичайно, це параметр -T, і це означає, що читати імена файлів зі стандартного вводу: Якщо ви даєте один тире як ім'я файлу `--files-from ', (тобто ви вказуєте або --files-from = - або -T -), тоді імена файлів читаються зі стандартного вводу
Стів Келет

14

Можливо, є ще один спосіб досягти того, що ви хочете. В основному,

  1. За допомогою команди find виведіть шлях до файлів, які ви шукаєте. Перенаправляємо stdout на ім'я файлу, що ви обрали.
  2. Потім натисніть кнопку з опцією -T, яка дозволяє їй приймати список розташувань файлів (той, який ви тільки що створили за допомогою find!)

    find . -name "*.whatever" > yourListOfFiles
    tar -cvf yourfile.tar -T yourListOfFiles
    

Тут є відповідь, як обробляти імена файлів з новими рядками в них: superuser.com/a/513319/151261
tommy.carstensen

8

Спробуйте запустити:

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

7

Чому ні:

tar czvf backup.tar.gz *

Впевнений, що розумно використовувати find, а потім xargs, але ти робиш це важко.

Оновлення: Porges прокоментував варіант пошуку, який, на мою думку, є кращою, ніж моя відповідь, або інший: find -print0 ... | xargs -0 ....


Мій повний код створить резервну копію лише елементів, змінених за минулу добу. З часу щоденного резервного копіювання я не хочу мати повторної інформації, щоб економити на розмірі файлу (у мене також є повна резервна копія кожні 15 днів).
Калеб Кестер

Щоб зробити це кращим питанням SO, я б поставив питання про "надійне використання пошуку, xargs та tar разом". Ваш заголовок і запитання насправді не вказують, що вам потрібно знайти і xargs, і все-таки ви це робите.
Warren P

xargs ... tar c ...замінить перший створений архів, якщо список файлів занадто довгий і xargsвиконується tarвдруге! Щоб уникнути перезапису, ви можете використовувати, xargs -xале тоді архів може бути неповним. Альтернативою може бути спочатку, tar c ...а потім, можливо, повторно tar r .... (мій внесок у надійність :)
pabouk

3

Якщо у вас є декілька файлів або каталогів, і ви хочете скопіювати їх у незалежний *.gzфайл, ви можете це зробити. За бажанням-type f -atime

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

Це стиснеться

httpd-log01.txt
httpd-log02.txt

до

httpd-log01.txt.gz
httpd-log02.txt.gz



2

Додав би коментар до публікації @Steve Kehlet, але потрібно 50 повторів (RIP).

Для тих, хто знайшов цю публікацію через численні гуглінг, я знайшов спосіб не тільки знайти конкретні файли з урахуванням часового діапазону, але і НЕ включити відносні шляхи АБО пробіли, які могли б спричинити помилки таргетування. (ДЯКУЄТЕ ТАКОЖ СТОЛОМ.)

find . -name "*.pdf" -type f -mtime 0 -printf "%f\0" | tar -czvf /dir/zip.tar.gz --null -T -
  1. . відносний каталог

  2. -name "*.pdf" шукайте pdfs (або будь-який тип файлу)

  3. -type f тип шукати - це файл

  4. -mtime 0 шукайте файли, створені за останні 24 години

  5. -printf "%f\0"Регулярно -print0АБО -printf "%f"НЕ працював для мене. З чоловічих сторінок:

Це цитування виконується так само, як і для GNU ls. Це не той самий механізм котирування, який використовується для -ls та -fls. Якщо ви можете вирішити, який формат використовувати для виводу знаходження, тоді зазвичай краще використовувати "\ 0" як термінатор, ніж використовувати новий рядок, оскільки імена файлів можуть містити пробіл та символи нового рядка.

  1. -czvf створити архів, відфільтрувати архів через gzip, дослівно перелічити файли, що обробляються, ім’я архіву

Редагувати 2019-08-14: Я хотів би додати, що я також міг використовувати фактично таку ж команду у своєму коментарі, просто використовуючи tar:

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

На --ignore-failed-readсьогоднішній день не було нових PDF-файлів.


1

Найкращим рішенням, здається, є створення списку файлів, а потім архівування файлів, оскільки ви можете використовувати інші джерела та робити щось інше зі списком.

Наприклад, це дозволяє використовувати список для обчислення розміру файлів, що архівуються:

#!/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

Один лайнер для цього?
Робіно
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.