Найдите файлы и запустите их (с пробелами)

110

Хорошо, здесь такая простая проблема. Я работаю над простым резервным кодом. Он отлично работает, за исключением случаев, когда в файлах есть пробелы. Вот как я нахожу файлы и добавляю их в tar-архив:

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

Проблема в том, что в имени файла есть пробел, потому что tar думает, что это папка. В принципе, есть ли способ добавить кавычки к результатам поиска? Или другим способом исправить это?

Калеб Кестер
источник
12
Лучший способ использования find ... | xargs ...заключается в использовании -print0 / -0 параметра на каждом: find -print0 ... | xargs -0 .... Это приведет к тому, что имена файлов будут разделены нулевым символом, что означает, что в ваших именах файлов могут быть пробелы, новые строки или другие странные вещи, и это все равно будет работать.
porges 05
8
Существует проблема с использованием xargs и tar таким образом, когда у вас большое количество файлов, xargs будет многократно вызывать tar -c, и это будет продолжать перезаписывать ваш архив, и в результате у вас не будет всех файлов, которые вы ожидаете . См. Это более подробное объяснение и мой ответ ниже.
Стив Кехлет,

Ответы:

217

Использовать это:

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

Так и будет:

  • работать с файлами с пробелами, новой строкой, дефисами в начале и прочими забавными вещами
  • обрабатывать неограниченное количество файлов
  • не будет многократно перезаписывать ваш backup.tar.gz, как при использовании tar -cwith, xargsбудет делать, когда у вас большое количество файлов

Также см:

Стив Кехлет
источник
1
как бы вы это сделали, если бы сначала захотели пропустить свою находку через sed несколько раз? например найти. -print0 | sed / backups / d | 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 -
Джон
1
@Steve, не могли бы вы объяснить, что такое параметр '-' в конце команды tar. Я не могу найти его на странице руководства GNU tar.
shaffooo 01
Конечно, это параметр для -T, и он означает чтение имен файлов из стандартного ввода: если вы укажете одно тире в качестве имени файла для `--files-from '(то есть вы укажете либо --files-from = - или -T -), то имена файлов считываются из стандартного ввода
Стив Кехлет 02
14

Может быть другой способ добиться желаемого. В принципе,

  1. Используйте команду find, чтобы вывести путь к любым файлам, которые вы ищете. Перенаправьте stdout на имя файла по вашему выбору.
  2. Затем tar с параметром -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 
gsteff
источник
7

Почему нет:

tar czvf backup.tar.gz *

Конечно, было бы разумно использовать find, а затем xargs, но вы делаете это трудным путем.

Обновление: Поргес прокомментировал вариант поиска, который, на мой взгляд, является лучшим ответом, чем мой ответ или другой: find -print0 ... | xargs -0 ....

Уоррен П.
источник
Мой полный код будет создавать резервные копии только тех элементов, которые были изменены за последний день. Поскольку это ежедневное резервное копирование, я не хочу иметь повторяющуюся информацию, чтобы сэкономить на размере файла (у меня также есть полная резервная копия каждые 15 дней).
Калеб Кестер
Чтобы сделать этот вопрос лучше SO, я бы задал вопрос о «надежном использовании вместе find, xargs и tar». Ваш заголовок и вопрос на самом деле не указывают, что вам нужны find и xargs, но вы это делаете.
Уоррен П.
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

Почему бы не попробовать что-то вроде этого: tar cvf scala.tar `find src -name *.scala`

Фрэнк Эггинк
источник
2

Другое решение, как здесь :

find var/log/ -iname "anaconda.*" -exec tar -cvzf file.tar.gz {} +
tommy.carstensen
источник
2

Хотел бы добавить комментарий к сообщению @Steve Kehlet, но потребуется 50 повторений (RIP).

Для всех, кто нашел этот пост через многочисленные поисковые запросы, я нашел способ не только найти определенные файлы с заданным временным диапазоном, но и НЕ включать относительные пути ИЛИ пробелы, которые могут вызвать ошибки tarring. (БОЛЬШОЕ СПАСИБО СТИВ.)

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

  2. -name "*.pdf" ищите PDF-файлы (или файлы любого типа)

  3. -type f тип для поиска - это файл

  4. -mtime 0 искать файлы, созданные за последние 24 часа

  5. -printf "%f\0"Обычное -print0ИЛИ -printf "%f"у меня НЕ сработало. Со страниц руководства:

Это цитирование выполняется так же, как и для GNU ls. Это не тот же механизм цитирования, который используется для -ls и -fls. Если вы можете решить, какой формат использовать для вывода find, то обычно лучше использовать '\ 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-файлов.

пользователь3472383
источник
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
Nux
источник
Один лайнер для этого?
Робино