Як поєднати команду 'tar' з 'find'


31

Команда find дає такий вихід:

[root @ localhost /] # знайти var / log / -іна анаконда. *
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Після поєднання з дьогтем він показує такий вихід:

[root @ localhost /] # find var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Але перераховуючи файл tar, він показує лише один файл

[root @ localhost /] # tar -tvf file.tar
-rw ------- root / root 208454 2012-02-27 12:01 var / log / anaconda.storage.log

Що я тут роблю неправильно?

З xargs я отримую цей вихід:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf file1.tar

Друге питання

Вводячи / перед var, означає, find /var/logчому він надає цей месааг- тар: Видалення провідної `/ 'з імен учасників

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.log
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.xlog
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.yum.log
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.syslog
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.program.log
tar: видалення провідних `/ 'з імен членів
/var/log/anaconda.storage.log

У простому вигляді яка різниця між наступними двома?

find var/log і find /var/log


Це напівфабрикатна тема, але, продовжуючи findкоманду, слід вказати пошуковий термін. Це працює без інколи, але не завжди.
nerdwaller

1
Якщо ви використовуєте {} +замість {} \;нього, групуйте результати пошуку в один аргумент
Jason S

Відповіді:


39

Примітка. Дивіться відповідь @ Iain щодо дещо ефективнішого рішення.

Зверніть увагу, що findбуде викликати -execдію для кожного знайденого файлу .

Якщо ви працюєте tar -cvf file.tar {}для кожного окремого findвиводу файлу , це означає, що ви будете перезаписувати file.tarкожного разу, що пояснює, чому ви закінчуєте лише один архів, який містить лише anaconda.storage.log- це останні файли find.

Тепер ви насправді хочете додати файли до архіву, а не створювати його щоразу (це -cробить параметр). Отже, використовуйте наступне:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

Ця -rопція додається до архіву, а не створювати його кожен раз.

Примітка: Замініть -iname anaconda.*на -iname "anaconda.*". Зірочка - це майна і може бути розширена вашою оболонкою, перш ніж вона findнавіть побачить її. Щоб запобігти цьому розширенню, загортайте аргумент у подвійні лапки.


Що стосується tarвидалення ведучих /: Архів повинен містити лише відносні імена файлів. Якщо ви додали файли з провідними /, вони зберігатимуться як абсолютні імена файлів, буквально означаючи, наприклад, /var/…на вашому комп'ютері.

IIRC - це просто застереження для tarінших програм, ніж GNU, і це безпечніше таким чином, оскільки ви не будете перезаписати фактичні дані, /var/…коли витягаєте архів, якщо він містить відносні імена файлів.


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

2
Щоправда, але я думаю, що ми можемо сміливо ігнорувати цю ситуацію;)
slhck

@slhck * - це майна, яка повинна відповідати всім можливостям? але тут find /var/log/ -iname anaconda*нічого не дають і не find /var/log/ -iname anaconda.*дають результату, чому?
макс

Коли ви будете вживати підстановку, її більше не побачать find. Отже, якщо у вас є anaconda*, а у вашій поточній папці є щось, на зразок якого, наприклад, anaconda5(що відповідає цій підстановці), підстановочний знак буде розширено, і findвін побачить -iname anaconda5замість -iname anaconda*. Чому перший не працює, а другий, залежить від того, які файли є у вашому поточному каталозі. @max
slhck

2
Ви можете використовувати {} +замість того, {} \;щоб згрупувати результати пошуку в один аргумент
Джейсон S,

41

Ви можете використовувати щось на кшталт:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

-print0І -Tпрацювати разом , щоб імена файлів з пробілами переклади рядків і т.д. Остаточний -говорить дьоготь читати вхідні імена файлів зі стандартного вводу.

Зверніть увагу , що -print0повинні прийти в кінці вашого заяви, за цей відповідь . Інакше ви, ймовірно, отримаєте більше файлів, ніж ви очікували.


2
Ви пропустили -nameваріант, викликаючи рішення для tarвсього каталогу. Якщо це те, що ви хочете, ви могли б зробити це простіше, як зовсім tar -cvf file.tar var/logне користуючись find.
Ніколь Гамільтон

2
+1 Розділення списку tar- хороша ідея. Це, безумовно, найкраще рішення, якщо ви очікуєте, що в іменах шляхів можуть бути пробіли. Я б навіть охарактеризував це як найкраще в технічному плані, оскільки він є надійним і ефективним. Але це вимагає додаткових спеціальних знань і про, findі про tar. Я більше віддаю перевагу заміщення команд лише тому, що це більш загальний інструмент: навчитися користуватися ним один раз, а потім використовувати його всюди. (Але я визнаю, я в Windows із оболонкою, де це завжди працює.) Вибачте, якщо мені здалося грубим.
Ніколь Гамільтон

2
Ви вже отримали +1. Будь щасливий. :) Довгі командні рядки завжди є основою створення процесу i / f в будь-якій ОС. Я пам’ятаю, як сперечався з Марком Луковським у Microsoft на початку 90-х, що обмеження символів Unicode на 32K для NT занадто мало, і він скаржився, що я не мав уявлення, скільки ще байтів знадобиться для зберігання довжин, а не шортів скрізь у ядрі. . Зітхнути. Більш загальні рішення для випадків, коли список аргументів занадто довгий, - це робити більше в оболонці (якщо можливо; у мене це є) або використовувати xargs.
Ніколь Гамільтон

9
якщо ви використовуєте -print0опцію find , вам також потрібен --nullваріант смоли .
mivk

2
І --no-unquoteвиявляється, що це теж потрібно: імена файлів, що містять звороту косу рису, інакше були б неправильно оброблені. (Ні, це не гіпотетично - я справді створюю архів дьогтю з чужого коду, що містить ім'я файлу із зворотними косою рисою в імені. Це я дізнався.)
hvd,

12

Спробуйте це:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

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

Використовуючи підстановку команд за допомогою зворотних посилань (або використовуючи $(...)позначення, якщо вам зручніше), весь список імен, що створюються, findвставляється назад в командний рядок як аргументи tar, змушуючи їх записувати їх відразу.


2
Це може закінчитися погано, якщо знайти вихідні файли з пробілами в їх імені, нові рядки або символи, що поширюються. Це може бути невдалим - трубопроводи з findрідко зустрічаються хорошою ідеєю. mywiki.wooledge.org/ParsingLs
slhck

3
@slhck, розміщення каналу від знаходження насправді, як правило, є хорошою ідеєю, як це дуже чітко пояснено на сторінці, на яку ви посилаєтесь у своєму коментарі :). Насправді це рекомендований спосіб робити речі. Ви просто повинні використовувати деякі прийоми (наприклад, read -rз -print0) , як зробив я в своїй відповіді.
тердон

4
@slhck Саме тому імена файлів і каталогів в Unix та Linux традиційно уникають пробілів в іменах. Ось чому, в Windows, де імена з пробілами поширені, я додав додаткове позначення заміни команд до власної оболонки Hamilton C, використовуючи подвійні зворотні посилання, які обробляють цілі рядки (можливо, включаючи пробіли) як єдині слова, які потрібно вставити назад в команду рядок. На жаль, жодна з оболонок Unix не має такої особливості.
Ніколь Гамільтон

1
Вони, можливо, традиційно уникали цього, але, створюючи файли в просторі користувачів через графічні інтерфейси, ви більше не можете нехтувати файлами з пробілами і ставитися до них як до громадян другого класу (тільки тому, що це Unix). Приємно, що ви включили це до своєї оболонки, але це для Windows, а оболонки Unix особливо не потребують цієї функції, якщо ви просто використовуєте правильний синтаксис і вживаєте належних заходів безпеки. Ось чому я опублікував свій коментар в першу чергу.
slhck

2
Ні, але в інших місцях це може бути дуже добре. Ось чому корисно програмувати захисно - краще будьте безпечні, ніж вибачте. Крім того, відвідувачі, які знайшли це питання, не обов'язково матимуть ту саму проблему, і дивуються, чому команда, яку вони знайшли тут, виявилася для цього саме випадку, але не вдалася до них. Я залишу це вам, щоб виправити команду, я просто вважав, що це важливо згадати, тому що багато людей рано чи пізно стикаються з цим питанням.
slhck

6

питання 1

Ваша команда не працює, оскільки tarбере кожен із знайдених файлів і архівує їх file.tar. Кожен раз, коли це буде зроблено, він буде перезаписувати створене раніше file.tar.

Якщо вам потрібен один архів з усіма файлами, просто запускайте tarбезпосередньо, немає необхідності find(і так, це працює для файлів з пробілами в їх назвах):

tar -vcf file.tar /var/log/anaconda*   

Питання 2

Дві команди абсолютно різні:

  • Знайти var / log буде шукати каталог, var/log який називається підкаталогом вашої поточної директорії , він еквівалентний find ./var/log(зверніть увагу ./).

  • знайти / вар / журнал буде шукати каталог з ім'ям , /var/log який є підкаталогом кореня/ .

Провідне /повідомлення від tar, не find. Це означає, що це видалення першого /з ваших імен файлів, щоб зробити абсолютні шляхи відносними . Це означає, що файл з файлу /var/log/anaconda.errorбуде вилучено ./var/log/anaconda.errorпід час зняття архіву.


1

Є два способи -execроботи. Один із способів виконує команду багато разів - один раз для кожного файлу; інший спосіб запускає команду один раз, включаючи всі файли як список параметрів.

  • -exec tar -cvf file.tar {} ';'запускає tarкоманду для кожного файлу, переписуючи архів кожного разу.
  • -exec tar -cvf file.tar {} '+'запускає tarкоманду один раз, створюючи архів усіх знайдених файлів.

1

Я думаю, що використання -exec для кожного файлу може зробити стиснення смоли дуже повільним, якщо у вас багато файлів. Я вважаю за краще використовувати команду:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar

поки вона не починається з відмови/bin/cpio: xxx: Cannot open: Too many open files
SYN
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.