tar --exclude не исключает. Почему?


71

У меня есть очень простая строка в bash-скрипте, которая успешно выполняется (т.е. создает_data.tar файл), за исключением того, что она не исключает подкаталоги, которые, как ей говорят, исключают через --excludeопцию:

/bin/tar -cf /home/_data.tar  --exclude='/data/sub1/*'  --exclude='/data/sub2/*' --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'  /data

Вместо этого он создает _data.tarфайл, который содержит все в каталоге / data, включая файлы в подкаталогах, которые я хотел исключить.

Есть идеи почему? и как это исправить?

Обновление Я реализовал свои наблюдения на основе ссылки, приведенной в первом ответе ниже (каталог верхнего уровня сначала, без пробела после последнего исключения):

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1/*'  --exclude='/data/sub2/*'  --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'

Но это не помогло. Все «исключенные» подкаталоги присутствуют в результирующем _data.tarфайле.

Это озадачивает. Является ли это ошибкой в ​​текущем tar (GNU tar 1.23, на CentOS 6.2, Linux 2.6.32) или «чрезвычайной чувствительностью» tar к пробелам и другим простым опущенным опечаткам, я считаю это ошибкой. Сейчас.

Это ужасно : я попробовал идею, предложенную ниже (без трейлинга /*), и она все еще не работает в производственном скрипте:

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1'  --exclude='/data/sub2'  --exclude='/data/sub3'  --exclude='/data/sub4'

Я не вижу никакой разницы между тем, что я пробовал, и тем, что пытался @Richard Perrin, за исключением кавычек и двух пробелов вместо 1. Я собираюсь попробовать это (нужно дождаться запуска ночного скрипта в качестве резервного каталога) вверх огромен) и доложить

/bin/tar -cf /home/_data.tar  /data --exclude=/data/sub1 --exclude=/data/sub2 --exclude=/data/sub3 --exclude=/data/sub4

Я начинаю думать, что все эти tar --excludeчувства - не смола, а что-то в моем окружении, но что тогда это может быть?

Это сработало! Последний вариант (без одинарных кавычек и одного пробела вместо двойного пробела --exclude) протестировал работу. Странно, но принимать.

Невероятно! Оказывается, что более старая версия tar(1.15.1) будет исключена, только если каталог верхнего уровня является последним в командной строке. Это полная противоположность тому, как требует версия 1.23. FYI.

Ответы:


50

Если вы хотите исключить весь каталог, ваш шаблон должен соответствовать этому каталогу, а не файлам в нем. Используйте --exclude=/data/sub1вместо--exclude='/data/sub1/*'

Будьте осторожны с цитированием шаблонов, чтобы защитить их от расширения оболочки.

Посмотрите на этот пример с проблемой в последнем вызове:

$ for i in 0 1 2; do mkdir -p /tmp/data/sub$i; echo foo > /tmp/data/sub$i/foo; done
$ find /tmp/data
/tmp/data
/tmp/data/sub2
/tmp/data/sub2/foo
/tmp/data/sub0
/tmp/data/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude='/tmp/data/sub[1-2]'
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub2/
/tmp/data/sub2/foo
/tmp/data/sub0/
/tmp/data/sub0/foo
/tmp/data/sub2/
tar: Removing leading `/' from hard link targets
/tmp/data/sub2/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub1 /tmp/data/sub2

Спасибо за очень сфокусированный и четкий ответ. Что касается вашего 1-го пункта, я пытался следовать советам в этой теме LQ . Я не уверен, что я пропустил, но теперь, когда я прочитал ваш 2-й пункт, вполне может быть проблема абсолютного и относительного пути. Я попробую это и доложу. +1 сейчас.
ateiob

Еще одна вещь, которую я заметил, это --exclude b(пробел вместо знака равенства) против --exclude=b. Это имеет какое-то значение? (не должен ИМХО)
ateiob

1
Знак равенства может быть необходим, чтобы избежать расширения оболочки без кавычек. Если вместо этого у вас есть пробел, то оболочка может быть расширена шаблоном без кавычек в один аргумент --exclude, а остальные расширения будут представлены в виде файлов для добавления в файл tar. Прежде всего, ваши примеры имеют '=' - если в сценарии этого нет и в нем отсутствуют одинарные кавычки, то это может стать источником вашей проблемы.
Р Перрин

ХОРОШО. Я проверил ваш пример на своем боксе, и он работает, даже с несколькими --exclude=на одной строке. Так что разница должна быть идиотской, /*которую я добавил в каждый подкаталог. Я проверю это сегодня вечером в производственном сценарии и сообщу. Еще +1.
ateiob

Для меня ответ от @carlo был специфической проблемой - тупой tar не может принять - исключить в качестве последней опции в командной строке - очевидно, вызывая много головных болей. Спасибо всем.
moodboom

34

Возможно, ваша версия tarтребует, чтобы --excludeопции были помещены в начале tarкоманды.

Смотрите: https://stackoverflow.com/q/984204

tar --exclude='./folder' --exclude='./upload/folder2' \
    -zcvf /backup/filename.tgz .

Смотрите: http://mandrivausers.org/index.php?/topic/8585-multiple-exclude-in-tar/

tar --exclude=<first> --exclude=<second> -cjf backupfile.bz2 /home/*

Альтернатива:

EXCLD='first second third'
tar -X <(for i in ${EXCLD}; do echo $i; done) -cjf backupfile.bz2 /home/*

Еще одна tarподсказка команды отсюда :

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject

Смотрите мое обновление выше. Последний вариант (без кавычек, один пробел) работает. Понятия не имею почему. +1 за хорошо продуманный ответ + ссылки.
ateiob

К вашему сведению, под debian, если я не уточню фильтр, как --exclude=mydir/*тогда, он не работает (используя tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Оливье Понс

1
@OlivierPons, а не «под Debian», или, возможно, с ним, поставить версию tar ( tar --version); Предположительно, Debian будет поставляться с различными версиями tar на протяжении многих лет.
апреля

1
Моя версия (1.29) работает только --excludeраньше -czf.
falsePockets

8

Для исключения нескольких файлов попробуйте

--exclude=/data/{sub1,sub2,sub3,sub4}

Это сэкономит некоторый код и головную боль. Это глобальное решение для всех видов программ / опций. Если вы также хотите включить родительский каталог в ваш выбор (в данном случае данные), вы должны добавить запятую. Например:

umount /data/{sub1,sub2,}

3
Я люблю кудряшки. Я считаю, что многие люди не знают о них, даже с многолетним опытом работы в Unix. mv /very/very/very/very/long/path/to/a/file{,.bak}
MST

5

Эта ссылка может быть полезной. http://answers.google.com/answers/threadview/id/739467.html

Два немедленных различия между нерабочей линией и некоторыми подсказками в ссылке:

  1. Все исключения идут после каталога верхнего уровня.
  2. Не может быть НИКАКИХ пробелов после последнего --exclude.

Благодарю. Ответ -MAKпривлек мое внимание, и до сих пор я смог обнаружить следующие различия между моей нерабочей строкой и следующим: 1. Все исключения идут после каталога верхнего уровня. 2. Не может быть никаких пробелов после последнего --exclude. Я проверю эти идеи и сообщу. +1 сейчас.
ateiob

@ateiob Если вы это выясните, вы можете опубликовать ответ здесь или отредактировать его? Как правило, мы предпочитаем не иметь ответы, которые просто ссылки в другом месте
Майкл Мрозек

@ Майкл Мрозек Абсолютно. Это именно то, что я написал в своем комментарии. :)
ateiob

3

Обходной путь может состоять в том, чтобы использовать комбинацию find ... -pruneи tarисключать указанные каталоги.

В Mac OS X --excludeопция GNU, tarпохоже, работает так, как должна.

В следующем тестовом примере каталоги /private/var/log/aslи /private/var/log/DiagnosticMessagesдолжны быть исключены из сжатого архива /private/var/logкаталога.

# all successfully tested in Bash shell on Mac OS X (using gnutar and gfind)

# sudo port install findutils  # for gfind from MacPorts

sudo gnutar -czf ~/Desktop/varlog.tar.gz /private/var/log --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages"

sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages" /private/var/log

set -f # disable file name globbing
sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/Diagnostic*" /private/var/log

# combining GNU find and tar (on Mac OS X)

sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "DiagnosticMessages" \) -prune -o -print0 | 
   sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -

# exclude even more dirs
sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "[Dacfks]*" \) -prune -o -print0 | 
    sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -


# testing the compressed archive

gnutar -C ~/Desktop -xzf ~/Desktop/varlog.tar.gz

sudo gfind /private/var/log ~/Desktop/private \( -iname DiagnosticMessages -or -iname asl \)

sudo rm -rf ~/Desktop/varlog.tar.gz ~/Desktop/private

Спасибо +1 за предложение. На данный момент я все еще пытаюсь понять, почему хорошо документированная (и зрелая) функция не работает в моем скрипте, запускаемом ночью cron.
ateiob

3

Возможно, вы можете попробовать команду с другой опцией:

--wildcards

И проверьте, работает ли он так, как задумано.


Смотрите мое обновление выше. Последний вариант (без кавычек, один пробел) работает. Понятия не имею почему. +1 за идею.
ateiob

3

Я использую Mac и обнаружил, что исключение не работает, если папка верхнего уровня не является последним аргументом

пример рабочей команды:

tar czvf tar.tgz --exclude='Music' dir

FYI:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3

То же самое касается tar 1.27.1 через Ubuntu 14.04.
Грег Белл

3

В моем случае это не исключало по другой причине.

Полный путь против относительного пути.

И исключение, и каталог должны использовать один и тот же формат пути (то есть как полный путь, так и оба относительных пути).

Пример:

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' ctms-db-sync

Это не будет работать, потому что исключение использует полный путь, где в качестве цели используется относительный путь

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' /home/mine/tmp/ctms-db-sync

Это работает, потому что оба используют полный путь

tar -cvf ctms-db-sync.tar --exclude='ctms-db-sync/sql' ctms-db-sync

Это работает, потому что оба используют относительный путь



1

Дополнительные примечания к превосходному ответу Р. Перрина :

Предположим, вы не хотите архивировать абсолютные, но относительные пути, например, «данные» вместо «/ tmp / data». Чтобы исключить абсолютные пути, ваши аргументы tar будут отличаться в зависимости от реализации tar (gnu tar против bsd tar), которую вы используете:

$ for i in 0 1 2; do
    for j in 0 1 2; do 
      mkdir -p /tmp/data/sub$i/sub$j
      echo foo > /tmp/data/sub$i/sub$j/foo
    done
  done

$ find /tmp/data/
/tmp/data/
/tmp/data/sub2
/tmp/data/sub2/sub2
/tmp/data/sub2/sub2/foo
/tmp/data/sub2/sub1
/tmp/data/sub2/sub1/foo
/tmp/data/sub2/sub0
/tmp/data/sub2/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/sub2
/tmp/data/sub1/sub2/foo
/tmp/data/sub1/sub1
/tmp/data/sub1/sub1/foo
/tmp/data/sub1/sub0
/tmp/data/sub1/sub0/foo
/tmp/data/sub0
/tmp/data/sub0/sub2
/tmp/data/sub0/sub2/foo
/tmp/data/sub0/sub1
/tmp/data/sub0/sub1/foo
/tmp/data/sub0/sub0
/tmp/data/sub0/sub0/foo

$ cd /tmp/data; tar -zvcf /tmp/_data.tar --exclude './sub[1-2]'
./
./sub0/
./sub0/sub2/
./sub0/sub2/foo
./sub0/sub1/
./sub0/sub1/foo
./sub0/sub0/
./sub0/sub0/foo

# ATTENTION: bsdtar's behaviour differs from traditional tar (without a leading '^')!
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude './sub[1-2]' .
a .
a ./sub0
a ./sub0/sub0
a ./sub0/sub0/foo

# FIX: Use a regex by adding a leading '^' will cause bsdtar to match only parent files and folders.
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
# ALTERNATIVE: bsdtar -C /tmp/data -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
a .
a ./sub0
a ./sub0/sub2
a ./sub0/sub1
a ./sub0/sub0
a ./sub0/sub0/foo
a ./sub0/sub1/foo
a ./sub0/sub2/foo

1

Только что обнаружен на tar (GNU tar) 1.29

Этот вызов не исключает из архивных файлов, указанных с --exclude-from:

/bin/tar --files-from ${datafile} --exclude-from ${excludefile} -jcf ${backupfile}

Этот звонок работает правильно:

/bin/tar --exclude-from ${excludefile} --files-from ${datafile} -jcf ${backupfile}

Порядок параметров важен!


0

Я перепробовал все виды комбинаций, включая некоторые из перечисленных ответов, и просто не мог получить их, чтобы исключить перечисленные файлы.

Итак, сыт по горло погоней за ответом на то, что должно было быть пятиминутной работой, я сделал наоборот: создал архив папок, которые я хотел включить.

Я сделал это, создав архив и добавив в него :

tar -cvpf /path/to/mybackup.tar ./bin
tar rvf /path/to/mybackup.tar ./boot
tar rvf /path/to/mybackup.tar ./etc
tar rvf /path/to/mybackup.tar ./home
tar rvf /path/to/mybackup.tar ./lib
tar rvf /path/to/mybackup.tar ./sbin
tar rvf /path/to/mybackup.tar ./usr
tar rvf /path/to/mybackup.tar ./var

Несколько заметок:

  • Я использовал относительные вместо абсолютных путей (которые также создавали проблемы), запустив их из корня файловой системы.
  • Вы должны создать простой tar(а не заархивированный tar .tgz/ .tar.gz) архив - вы можете сжать его позже, используяgzip mybackup.tar
  • Убедитесь, что вы не помещаете архив в какую-либо папку, в которую вы включаете, или вы получите некоторую рекурсию (частичная резервная копия также включена в саму резервную копию).
  • Обратите внимание на отличие первой команды (создать) от других (добавить).
  • Вы можете проверить, что файлы добавляются, а не резервная копия перезаписывается (например, после второй команды), если вы параноик, используя tar tvf mybackup.tar.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.