Come combinare il comando 'tar' con 'find'


31

Il comando find fornisce questo output:

[root @ localhost /] # find 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

Dopo aver combinato con tar sta mostrando questo output:

[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

Ma mentre elenca il file tar mostra solo un singolo file

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

Cosa sto facendo di sbagliato qui?

Con xargs sto ottenendo questo output:

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

Seconda domanda

Mentre si digita / davanti a var, significa find /var/logperché sta dando questo mesaage tar: Rimuovere i primi `/ 'dai nomi dei membri

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.log
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.xlog
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.yum.log
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.syslog
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.program.log
tar: rimozione del carattere `/ 'iniziale dai nomi dei membri
/var/log/anaconda.storage.log

In una forma semplice, qual è la differenza tra i seguenti due?

find var/log e find /var/log


Questo è un argomento semi + off, ma andando avanti con il findcomando, dovresti citare il termine di ricerca. Funziona senza a volte ma non sempre.
nerdwaller,

1
Se lo usi {} +invece {} \;, raggruppa i risultati di find in un argomento
Jason S

Risposte:


39

Nota: vedi la risposta di @ Iain per una soluzione un po 'più efficiente.

Si noti che findchiamerà l' -execazione per ogni singolo file trovato.

Se corri tar -cvf file.tar {}per ogni singolo findoutput di file , questo significa che sovrascriverai file.tarogni volta, il che spiega perché finisci con un archivio rimasto che contiene solo anaconda.storage.log- è l'ultimo filefind output del .

Ora, in realtà vuoi aggiungere i file all'archivio invece di crearlo ogni volta (questo è ciò che fa l' -copzione). Quindi, utilizzare quanto segue:

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

L' -ropzione aggiunge all'archivio invece di ricrearlo ogni volta.

Nota: sostituire -iname anaconda.*con -iname "anaconda.*". L'asterisco è un carattere jolly e può essere espanso dalla shell findanche prima di vederlo. Per impedire questa espansione, racchiudi l'argomento tra virgolette doppie.


Per quanto riguarda la tarrimozione di lead /: l'archivio dovrebbe contenere solo nomi di file relativi . Se si aggiungono file con un vantaggio /, verranno memorizzati come nomi di file assoluti , che significa letteralmente/var/… , ad esempio sul tuo computer.

IIRC è semplicemente una precauzione per tarimplementazioni diverse da GNU, ed è più sicuro in questo modo perché non sovrascriverete i vostri dati reali /var/…quando estraete l'archivio se contiene nomi di file relativi.


6
Ma nota che se provassi tarad un vero archivio di nastri in questo modo, aggiungendo un file alla volta, riavvolgendo il nastro e rileggendo ogni volta il tutto per arrivare alla fine, il tutto sarebbe ridicolmente lento. La tua soluzione è adatta solo se stai scrivendo il file tar su disco.
Nicole Hamilton,

2
È vero, ma penso che possiamo tranquillamente ignorare questa situazione;)
slhck,

@slhck * è un carattere jolly che dovrebbe corrispondere a tutte le possibilità, giusto? ma qui find /var/log/ -iname anaconda*non dare nulla e find /var/log/ -iname anaconda.*dare l'output, perché?
massimo

Quando viene utilizzato un carattere jolly, non verrà findpiù visualizzato. Quindi, se lo hai anaconda*, e nella tua cartella attuale c'è qualcosa chiamato, ad esempio, anaconda5(corrispondente a questo carattere jolly), il carattere jolly verrà espanso e findvedrà -iname anaconda5invece di -iname anaconda*. Perché il primo non funziona e il secondo dipende da quali file si trovano nella directory corrente. @max
slhck,

2
Puoi usare {} +invece di {} \;così raggrupperà i risultati di find in un argomento
Jason S

41

Puoi usare qualcosa come:

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

Il -print0e -Tlavorare insieme per consentire nomi di file con spazi newline, ecc. Il finale -dice a tar di leggere i nomi dei file di input da stdin.

Nota che -print0deve arrivare alla fine della tua dichiarazione, per questa risposta . Altrimenti probabilmente otterrai più file di quanto ti aspetti.


2
Hai omesso l' -nameopzione, causando la tua soluzione tarall'intera directory. Se è quello che vuoi, potresti farlo più facilmente tar -cvf file.tar var/logsenza usarlo findaffatto.
Nicole Hamilton,

2
+1 Piping l'elenco a tarè una buona idea. È sicuramente la soluzione migliore se ti aspetti che i nomi dei percorsi possano avere spazi. Lo descriverei anche come il migliore tecnicamente, poiché è sia affidabile che efficiente. Ma richiede una conoscenza speciale aggiuntiva di entrambi finde tar. Preferisco la sostituzione dei comandi praticamente solo perché è uno strumento più generale: scopri come usarlo una volta, quindi usalo ovunque. (Ma lo ammetto, sono su Windows con una shell dove funziona sempre.) Mi scuso se mi è sembrato maleducato.
Nicole Hamilton,

2
Hai già ottenuto il tuo +1. Siate felici. :) Le lunghe linee di comando sono sempre la rovina della creazione del processo i / f su qualsiasi sistema operativo. Ricordo di aver litigato con Mark Lucovsky alla Microsoft nei primi anni '90 che il loro limite di caratteri Unicode da 32 KB su NT era troppo piccolo e facendomi lamentare non avevo idea di quanti più byte ci sarebbero voluti per memorizzare lunghezze piuttosto che corti ovunque nel kernel . Sospiro. Le soluzioni più generali del caso quando l'elenco arg è troppo lungo sono fare di più nella shell (se possibile; nel mio lo è) o usare xargs.
Nicole Hamilton,

9
se usi l' -print0opzione find , hai anche bisogno --nulldell'opzione tar .
marzo

2
E --no-unquoterisulta anche necessario: i nomi dei file contenenti barre rovesciate verrebbero altrimenti mal gestiti. (No, questo non è un ipotetico - sto davvero creando un archivio tar dal codice di qualcun altro, contenente un nome file con barre rovesciate nel nome, ecco come l'ho scoperto.)
hvd

12

Prova questo:

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

Stavi cercando di utilizzare findper -exec tar. Ma come funziona l' -execopzione, esegue quel comando una volta per ogni file corrispondente che trova, causando la tarsovrascrittura del file tar che produce ogni volta. Ecco perché sei finito solo con l'ultimo. Inoltre, è necessario inserire le virgolette attorno al modello specificato in findmodo che la shell non lo espanda prima di passarlo find.

Usando la sostituzione dei comandi con i backtick (o usando la $(...)notazione se preferisci), l'intero elenco di nomi prodotti da findviene incollato sulla riga di comando come argomenti tar, facendoli scrivere tutti in una volta.


2
Questo potrebbe finire male se trova file di output con spazi nel loro nome, newline o caratteri globbing. Questo è destinato a fallire: eseguire il piping di stdout findraramente è una buona idea. mywiki.wooledge.org/ParsingLs
slhck

3
@slhck, piping stdout da find è in realtà di solito una buona idea, come spiegato molto chiaramente nella pagina a cui ti sei collegato nel tuo commento :). È in effetti il ​​modo consigliato di fare le cose. Dovresti semplicemente usare alcuni trucchi (come quelli read -rdi -print0) come ho fatto nella mia risposta.
Terdon

4
@slhck Ecco perché i nomi di file e directory in Unix e Linux hanno tradizionalmente evitato spazi nei nomi. È anche il motivo per cui, su Windows, dove i nomi con spazi sono comuni, ho aggiunto un'ulteriore notazione di sostituzione di comando alla mia shell Hamilton C usando doppi backtick che trattano le intere righe (possibilmente includendo spazi) come singole parole da incollare di nuovo sul comando linea. Sfortunatamente, nessuna delle shell Unix ha questa caratteristica.
Nicole Hamilton,

1
Potrebbero averlo tradizionalmente evitato, ma con i file creati nello spazio utente attraverso le GUI, non puoi più trascurare i file con spazi e trattarli come cittadini di seconda classe (solo perché è Unix). È bello che tu l'abbia incluso nella tua shell, ma è per Windows e le shell Unix non hanno particolarmente bisogno di quella funzionalità se usi semplicemente la sintassi giusta e prendi le dovute precauzioni. Ecco perché ho pubblicato il mio commento in primo luogo.
slhck,

2
No, ma in altri luoghi potrebbe benissimo accadere. Ecco perché è una buona idea programmare in modo difensivo - meglio prevenire che curare. Inoltre, i visitatori che trovano questa domanda potrebbero non avere necessariamente lo stesso identico problema e chiedersi perché il comando che hanno trovato qui sembra funzionare per questo caso, ma non è riuscito per loro. Lascio a te il compito di correggere il comando, ho solo pensato che fosse importante menzionarlo perché prima o poi molte persone incontrano questo problema.
slhck,

6

Domanda 1

Il tuo comando fallisce perché tarsta prendendo ciascuno dei file trovati e li sta archiviando file.tar. Ogni volta che lo fa, sovrascriverà il creato in precedenzafile.tar .

Se quello che vuoi è un archivio con tutti i file, esegui semplicemente tardirettamente, non è necessario find(e sì, questo funziona per i file con spazi nei loro nomi):

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

Domanda 2

I due comandi sono completamente diversi:

  • find var / log cercherà una directory chiamata var/log che è una sottodirectory della directory corrente , equivale a find ./var/log(nota il ./).

  • find / var / log cercherà una directory chiamata /var/log che è una sottodirectory della radice,/ .

Il /messaggio principale tarnon viene find. Significa che sta rimuovendo il primo /dei nomi dei file per rendere i percorsi assoluti in relativi . Ciò significa che il file da /var/log/anaconda.errorverrà estratto ./var/log/anaconda.errorquando si annulla l'archivio.


1

Esistono due modi in cui -execpuò funzionare. Un modo esegue il comando più volte - una volta per ogni file; l'altro modo esegue il comando una volta, includendo tutti i file come un elenco di parametri.

  • -exec tar -cvf file.tar {} ';' esegue il tar comando per ogni file, sovrascrivendo ogni volta l'archivio.
  • -exec tar -cvf file.tar {} '+'esegue il tarcomando una volta, creando un archivio di tutti i file trovati.

1

Penso che l'uso di -exec per ogni file possa rendere la compressione tar molto lenta, se hai molti file. Preferisco usare il comando:

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

fino a quando non inizia a fallire con/bin/cpio: xxx: Cannot open: Too many open files
SYN il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.