Как объединить команду «tar» с «find»

31

Команда find дает такой вывод:

[root @ localhost /] # find var / log / -iname anaconda. *
вар / Журнал / anaconda.log
вар / Журнал / anaconda.xlog
вар / Журнал / anaconda.yum.log
вар / Журнал / anaconda.syslog
вар / Журнал / anaconda.program.log
вар / Журнал / anaconda.storage.log

После объединения с tar он показывает следующий вывод:

[root @ localhost /] # find var / log / -имя anaconda. * -exec tar -cvf file.tar {} \;
вар / Журнал / anaconda.log
вар / Журнал / anaconda.xlog
вар / Журнал / anaconda.yum.log
вар / Журнал / anaconda.syslog
вар / Журнал / anaconda.program.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почему он дает этот mesaage tar: удаление начального `/ 'из имен членов

[root @ localhost /] # find / var / log / -имя 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/…при извлечении архива, если он содержит относительные имена файлов.

slhck
источник
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
Вы можете использовать {} + вместо этого, {} \;чтобы сгруппировать результаты поиска в один аргумент
Jason S
41

Вы можете использовать что-то вроде:

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

Они -print0и -Tработают вместе, чтобы разрешить имена файлов с пробелами, символами новой строки и т. Д. В последнем -сообщается, что tar читает входные имена файлов из stdin.

Обратите внимание, что -print0должно прийти в конце вашего утверждения, согласно этому ответу . В противном случае вы, вероятно, получите больше файлов, чем ожидаете.

Питер Мортенсен
источник
2
Вы пропустили эту -nameопцию, вызывая ваше решение для tarвсего каталога. Если это то, что вы хотите, вы можете сделать это проще, tar -cvf file.tar var/logне используя findвообще.
Николь Гамильтон
2
+1 Прикрепление списка tar- хорошая идея. Это определенно лучшее решение, если вы ожидаете, что пути могут содержать пробелы. Я бы даже назвал его лучшим с технической точки зрения, поскольку он надежен и эффективен. Но это требует дополнительных специальных знаний и того, findи другого tar. Я предпочитаю подстановку команд в значительной степени только потому, что это более общий инструмент: узнайте, как использовать его один раз, а затем используйте его везде. (Но я признаю, что у меня Windows с оболочкой, где она всегда работает.) Извиняюсь, если мне показалось грубым.
Николь Гамильтон
2
Вы уже получили +1. Будь счастлив. :) Длинные командные строки всегда являются бичом процесса создания i / f на любой ОС. Я помню, как спорил с Марком Луковским в Microsoft в начале 90-х, что их ограничение на 32K Unicode-символы в NT было слишком маленьким, и когда он жаловался, я понятия не имел, сколько еще байтов потребуется, чтобы хранить длины как long, а не shorts везде в ядре , Вздох. Более общие решения для случая, когда список аргументов слишком длинный, заключаются в том, чтобы делать больше в оболочке (если возможно; в моем случае это так) или использовать xargs.
Николь Гамильтон
9
если вы используете -print0опцию find , вам также нужна --nullопция tar .
Mivk
2
И --no-unquoteоказывается, что это необходимо: имена файлов, содержащие обратную косую черту, в противном случае были бы неправильно обработаны. (Нет, это не гипотетически - я действительно создаю архив tar из чужого кода, содержащий имя файла с
обратными косыми чертами
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, использование stdout из find обычно является хорошей идеей, что очень четко объясняется на странице, на которую вы ссылаетесь в своем комментарии :). На самом деле это рекомендуемый способ делать вещи. Вы должны просто использовать некоторые приемы (например, read -rиз -print0), как я сделал в своем ответе.
Тердон
4
@slhck Вот почему имена файлов и каталогов в Unix и Linux традиционно избегают пробелов в именах. Именно поэтому в Windows, где имена с пробелами являются общими, я добавил дополнительную запись подстановки команд в свою собственную оболочку Hamilton C, используя двойные обратные тики, которые обрабатывают целые строки (возможно, включая пробелы) как отдельные слова, которые вставляются обратно в команду. линия. К сожалению, ни одна из оболочек Unix не имеет такой возможности.
Николь Гамильтон
1
Они, возможно, традиционно избегали этого, но с файлами, созданными в пространстве пользователя через GUI, вы не можете больше пренебрегать файлами с пробелами и рассматривать их как граждан второго сорта (только потому, что это Unix). Хорошо, что вы включили это в свою оболочку, но это для Windows, и оболочкам Unix эта особенность особенно не нужна , если вы просто используете правильный синтаксис и принимаете надлежащие меры предосторожности. Вот почему я разместил свой комментарий в первую очередь.
Slhck
2
Нет, но в других местах это вполне может случиться. Вот почему неплохо программировать в обороне - лучше быть в безопасности, чем потом сожалеть. Кроме того, посетители, находящие этот вопрос, могут не обязательно сталкиваться с той же самой проблемой и удивляться, почему команда, которую они нашли здесь, показалась работающей для этого самого случая, но потерпела неудачу для них. Я оставлю это на ваше усмотрение, чтобы исправить команду, я просто подумал, что важно упомянуть это, потому что многие люди сталкиваются с этой проблемой рано или поздно.
Slhck
6

Вопрос 1

Ваша команда терпит неудачу, потому что tarберет каждый из найденных файлов и архивирует их file.tar. Каждый раз, когда это происходит, он перезаписывает ранее созданныйfile.tar .

Если вам нужен один архив со всеми файлами, то просто запустите tarнапрямую, в этом нет необходимости find(и да, это работает для файлов с пробелами в их именах):

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

вопрос 2

Две команды совершенно разные:

  • find var / log будет искать каталог с именем, var/log который является подкаталогом вашего текущего каталога , он эквивалентен find ./var/log(обратите внимание на ./).

  • найти / вар / журнал будет искать каталог с именем , /var/log который является подкаталогом корня/ .

Ведущее /сообщение от tar, а не find. Это означает, что он удаляет первое /из ваших имен файлов, чтобы превратить абсолютные пути в относительные . Это означает, что файл из /var/log/anaconda.errorбудет извлечен, ./var/log/anaconda.errorкогда вы распакуете архив.

terdon
источник
1

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

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

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

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
fabceolin
источник
пока это не начинает терпеть неудачу с/bin/cpio: xxx: Cannot open: Too many open files
SYN