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файл, який містить все під / дані, включаючи файли у підкаталогах, які я хотів виключити.

Будь-яка ідея чому? і як це виправити?

Оновлення Я реалізував свої спостереження на основі посилання, наведеного у першій відповіді нижче (перший реж. Dir, немає пробілів після останнього виключити):

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

Але це не допомогло. Усі "виключені" підкаталоги містяться в отриманому _data.tarфайлі.

Це дивовижно. Невже це помилка в поточному дьогті (GNU tar 1.23, на CentOS 6.2, Linux 2.6.32) або "надзвичайна чутливість" дьогтю до пробілів та інших помилок, які я легко пропускаю, я вважаю це помилкою. Зараз.

Це жахливо : я спробував уявлення, запропоноване нижче (без трейлінгу /*), і воно все ще не працює в сценарії виробництва:

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

Я не бачу різниці між тим, що я спробував, і тим, що намагався @ Richard Perrin, за винятком цитат та 2 пробілів замість 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) виключала б лише те, що dir верхнього рівня останній у командному рядку. Це абсолютно протилежне тому, як вимагає версія 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

Дякую за дуже цілеспрямовану та чітку відповідь. Щодо вашого першого пункту, я намагався дотримуватися підказок у цій темі LQ . Я не впевнений, що я пропустив, але тепер, коли я прочитав ваш другий пункт, це може бути абсолютно абсолютним проти відносного питання про шлях. Я спробую це і звіту. Поки що +1
ateiob

Ще одна річ , яку я помітив це --exclude b(пропуск замість знака рівності) проти --exclude=b. Чи має це значення? (не слід ІМХО)
ateiob

1
Знак рівності може бути суттєвим, щоб уникнути розширення оболонок без котируваних шаблонів. Якщо натомість у вас є пробіл, то без котируемого шаблону оболонку можна розширити в єдиний аргумент --exclude, а решта розширень подати як файли для додавання у файл tar. Ваші приклади, перш за все, мають '=' - якщо скрипт не має, і в ньому відсутні одиночні лапки, то це може стати джерелом вашої проблеми.
R Perrin

ГАРАЗД. Я перевірив ваш приклад на моїй коробці, і він працює, навіть з кратним --exclude=в одному рядку. Отже, різниця повинна бути ідіотичною, /*яку я додав до кожного підкаталогу. Я перевіряю це сьогодні у виробничому сценарії та звітую. Ще +1.
ateiob

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

32

Можливо, ваша версія 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

FYI, під debian, Якщо я не вказую точний фільтр, як --exclude=mydir/*тоді, він не працює (використовуючи tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Олів'є Понс

1
@OlivierPons замість "під debian" чи, можливо, разом з нею ставити версію tar ( tar --version); Debian, ймовірно, поставляється з багатьма різними версіями смоли протягом багатьох років.
msouth

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}
msouth

5

Це посилання може бути корисним. http://answers.google.com/answers/threadview/id/739467.html

Дві негайні відмінності між неробочою лінією та деякими порадами у посиланні:

  1. Усі виключення надходять після каталогу верхнього рівня.
  2. Не може бути жодного пробілу після останнього --exclude.

Дякую. Відповідь -MAKпривернула мою увагу, і поки що мені вдалося помітити наступні відмінності між моєю неробочою лінією та наступною: 1. Усі виключення надходять після каталогу верхнього рівня. 2. Не може бути жодного пробілу після останнього --exclude. Я перевірю ці відомості і звіт. Поки що +1
ateiob

@ateiob Якщо ви це зрозуміли, чи можете ви розмістити тут відповідь чи відредагувати цю? Ми, як правило, не маємо відповідей, які є лише посиланнями в іншому місці
Michael Mrozek

@Michael Mrozek Абсолютно. Це саме те, що я написав у своєму коментарі. :)
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 за пропозицію. На даний момент я все ще намагаюся зрозуміти, чому добре задокументована (і зріла) функція не працює в моєму сценарії, що вечір проводиться кроном.
атейоб

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 vs. 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

Щойно виявлено на дьогті (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.