Como combinar o comando 'tar' com 'find'

31

O comando find fornece esta saída:

[root @ localhost /] # encontra var / log / -iname anaconda. *
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

Depois de combinar com o tar, está mostrando esta saída:

[root @ localhost /] # encontra var / log / -nome 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

Mas ao listar o arquivo tar, ele mostra apenas um único arquivo

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

O que estou fazendo de errado aqui?

Com xargs, estou recebendo esta saída:

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

Segunda questão

Ao digitar / na frente de var, significa find /var/logpor que está dando a essa mesaage tar: Removendo `/ 'principal dos nomes dos membros

[root @ localhost /] # find / var / log / -nome anaconda. * -exec tar -cvf file.tar {} \;
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.log
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.xlog
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.yum.log
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.syslog
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.program.log
tar: Removendo `/ 'principal dos nomes dos membros
/var/log/anaconda.storage.log

De uma forma simples, qual é a diferença entre os dois seguintes?

find var/log e find /var/log

max
fonte
Este é um tópico semi-off, mas, adiante, com o findcomando, você deve citar o termo da pesquisa. Funciona sem às vezes, mas nem sempre.
Nerdwaller 01/12/12
1
Se você usar {} +, em vez de {} \;ele irá resultados do grupo de achado em um argumento
Jason S

Respostas:

39

Nota: Veja a resposta de @ Iain para uma solução um pouco mais eficiente.

Observe que findchamará a -execação para cada arquivo que encontrar.

Se você executar tar -cvf file.tar {}para cada findsaída de arquivo , isso significa que você será substituído file.tarsempre, o que explica por que você acaba com um arquivo que contém apenas anaconda.storage.log- são as últimas findsaídas de arquivo .

Agora, você realmente deseja anexar os arquivos ao arquivo morto em vez de criá-lo sempre (é o que a -copção faz). Então, use o seguinte:

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

A -ropção anexa ao arquivo morto em vez de recriá-lo sempre.

Nota: Substitua -iname anaconda.*por -iname "anaconda.*". O asterisco é um curinga e pode ser expandido pelo seu shell antes findmesmo de vê-lo. Para impedir essa expansão, coloque o argumento entre aspas duplas.


Quanto à tarremoção dos principais /: O arquivo deve conter apenas nomes de arquivos relativos . Se você incluísse arquivos à esquerda /, eles seriam armazenados como nomes absolutos , literalmente /var/…no seu computador, por exemplo.

IIRC: isso é simplesmente uma precaução para tarimplementações diferentes do GNU, e é mais seguro dessa maneira, porque você não substituirá seus dados reais /var/…quando extrair o arquivo, se ele contiver nomes de arquivos relativos.

slhck
fonte
6
Mas observe que, se você tentasse tarum arquivo de fita real dessa maneira, adicionando um arquivo de cada vez, rebobinando a fita e relendo a coisa toda a cada vez para chegar ao fim, tudo seria ridiculamente lento. Sua solução é adequada apenas se você estiver gravando o arquivo tar no disco.
Nicole Hamilton
2
É verdade, mas acho que podemos desconsiderar este situação;)
slhck
@slhck * é um curinga que deve corresponder a todas as possibilidades, certo? mas aqui find /var/log/ -iname anaconda*dando nada e find /var/log/ -iname anaconda.*dando a saída, por quê?
Max
Quando um curinga é consumido, ele não será mais visto find. Portanto, se você tiver anaconda*, e na sua pasta atual houver algo chamado, por exemplo, anaconda5(correspondente a este curinga), o curinga será expandido e findserá exibido em -iname anaconda5vez de -iname anaconda*. Por que o primeiro não funciona e o segundo depende de quais arquivos estão no diretório atual. @max
slhck 2/12/12
2
Você pode usar em {} +vez de, {} \;para que ele agrupe os resultados da busca em um argumento
Jason S
41

Você pode usar algo como:

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

O -print0e -Ttrabalham juntos para permitir que nomes de arquivos com espaços novas linhas, etc. A final -diz tar a ler os nomes dos arquivos de entrada de stdin.

Observe que -print0deve aparecer no final de sua declaração, de acordo com esta resposta . Caso contrário, você provavelmente obterá mais arquivos do que espera.

Peter Mortensen
fonte
2
Você omitiu a -nameopção, causando sua solução para tartodo o diretório. Se é isso que você deseja, você pode fazê-lo mais facilmente, tar -cvf file.tar var/logsem usar find.
Nicole Hamilton
2
Marcar a lista com +1 taré uma boa ideia. Definitivamente, é a melhor solução se você espera que os nomes de caminho possam ter espaços. Eu o descreveria como o melhor tecnicamente, pois é confiável e eficiente. Mas requer conhecimento especial adicional de ambos finde tar. Prefiro a substituição de comandos apenas porque é uma ferramenta mais geral: aprenda a usá-lo uma vez e depois em qualquer lugar. (Mas admito, estou no Windows com um shell onde sempre funciona.) Desculpas se parecesse rude.
Nicole Hamilton
2
Você já recebeu seu +1. Seja feliz. :) Linhas de comando longas são sempre a desgraça da criação do processo i / f em qualquer sistema operacional. Lembro-me de discutir com Mark Lucovsky na Microsoft, no início dos anos 90, que o limite de caracteres de 32K Unicode no NT era muito pequeno e que ele reclamasse. Não fazia ideia de quantos bytes mais seriam necessários para armazenar comprimentos de comprimentos, em vez de curtos, em todos os lugares do kernel. . Suspiro. As soluções de caso mais gerais, quando a lista arg é muito longa, devem fazer mais no shell (se possível; no meu, é) ou usar xargs.
Nicole Hamilton
9
se você usa a -print0opção find , também precisa da --nullopção tar .
mivk
2
E --no-unquoteacaba sendo necessário também: nomes de arquivos contendo barras invertidas seriam manipulados incorretamente. (Não, este não é um hipotético - Estou realmente criar um arquivo tar de outra pessoa código, contendo um nome de arquivo com barras invertidas no nome, é assim que eu descobri.)
HVD
12

Tente o seguinte:

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

Você estava tentando usar finda -exec tar. Mas, da maneira que a -execopção funciona, ele executa esse comando uma vez para cada arquivo correspondente encontrado, fazendo tarcom que sobrescreva o arquivo tar que ele produz a cada vez. É por isso que você acabou com o último. Além disso, você precisa colocar aspas no padrão especificado para findque o shell não o expanda antes de passá-lo para find.

Usando substituição de comando com reticulares (ou usando $(...)notação, se você preferir), toda a lista de nomes produzidos por findé colada de volta na linha de comando como argumentos para tar, fazendo com que ela escreva todos de uma vez.

Nicole Hamilton
fonte
2
Isso pode acabar ruim se encontrar arquivos de saída com espaços em seu nome, novas linhas ou caracteres brilhantes. É provável que isso ocorra - o stdout da tubulação findraramente é uma boa idéia. mywiki.wooledge.org/ParsingLs
slhck
3
@slhck, canalizar stdout de find é, de fato, geralmente uma boa idéia, conforme explicado com muita clareza na página que você vinculou no seu comentário :). Na verdade, é a maneira recomendada de fazer as coisas. Você deve usar alguns truques (como read -rde -print0) como fiz na minha resposta.
Ter12
4
@slhck É por isso que os nomes de arquivos e diretórios no Unix e Linux tradicionalmente evitam espaços nos nomes. Também é por isso que, no Windows, onde nomes com espaços são comuns, adicionei uma notação de substituição de comando adicional ao meu próprio shell Hamilton C usando backticks duplos que tratam as linhas inteiras (possivelmente incluindo espaços) como palavras únicas a serem coladas de volta no comando linha. Infelizmente, nenhum dos shells do Unix tem esse recurso.
Nicole Hamilton
1
Eles podem ter tradicionalmente evitado isso, mas com os arquivos sendo criados no espaço do usuário por meio de GUIs, você não pode mais negligenciar arquivos com espaços e tratá-los como cidadãos de segunda classe (apenas porque é o Unix). É bom você incluí-lo no seu shell, mas é para Windows, e os shell do Unix não precisam desse recurso se você simplesmente usar a sintaxe correta e tomar as devidas precauções. É por isso que eu postei meu comentário em primeiro lugar.
Slhck #
2
Não, mas em outros lugares isso pode muito bem acontecer. É por isso que é uma boa idéia programar defensivamente - é melhor prevenir do que remediar. Além disso, os visitantes que encontrarem essa pergunta podem não ter exatamente o mesmo problema e se perguntam por que o comando que encontraram aqui parece funcionar nesse caso, mas falhou para eles. Vou deixar que você conserte o comando, apenas achei importante mencioná-lo, porque muitas pessoas enfrentam esse problema mais cedo ou mais tarde.
Slhck
6

Questão 1

Seu comando falha porque tarestá pegando cada um dos arquivos encontrados e arquivando-os file.tar. Sempre que fizer isso, ele substituirá o criado anteriormente file.tar.

Se o que você deseja é um arquivo com todos os arquivos, basta executar tardiretamente, não há necessidade find(e sim, isso funciona para arquivos com espaços em seus nomes):

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

Questão 2

Os dois comandos são completamente diferentes:

  • O comando find var / log procurará em um diretório chamado var/log subdiretório do diretório atual , é equivalente a find ./var/log(observe o ./).

  • O find / var / log pesquisará um diretório chamado /var/log subdiretório da raiz/ ,.

A /mensagem principal é de tar, não find. Isso significa que ele está removendo o primeiro nome /de arquivo para transformar caminhos absolutos em relativos . Isso significa que o arquivo /var/log/anaconda.errorserá extraído para ./var/log/anaconda.errorquando você descompactar o arquivo morto.

Terdon
fonte
1

Existem duas maneiras de -execfuncionar. Uma maneira de executar o comando várias vezes - uma vez para cada arquivo; por outro lado, executa o comando uma vez, incluindo todos os arquivos como uma lista de parâmetros.

  • -exec tar -cvf file.tar {} ';'executa o tarcomando para cada arquivo, substituindo o arquivo cada vez.
  • -exec tar -cvf file.tar {} '+'executa o tarcomando uma vez, criando um arquivo morto de todos os arquivos encontrados.
mwfearnley
fonte
1

Eu acho que usar -exec para cada arquivo pode tornar a compactação do tar muito lenta, se você tiver muitos arquivos. Eu prefiro usar o comando:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
fabceolina
fonte
até que comece a falhar/bin/cpio: xxx: Cannot open: Too many open files
SYN