2010-04-23 5 views
21

Ho un lavoro in esecuzione sul mio server alla riga di comando per due giorni:modo più efficiente per trovare & tar milioni di file

find data/ -name filepattern-*2009* -exec tar uf 2009.tar {} ; 

sta prendendo sempre, e poi qualche. Sì, ci sono milioni di file nella directory di destinazione. (Ogni file è un misero 8 byte in una struttura di directory ben hash). Ma solo in esecuzione ...

find data/ -name filepattern-*2009* -print > filesOfInterest.txt 

... richiede solo due ore o giù di lì. Al ritmo del mio lavoro, non sarà finito per un paio di settimane .. Sembra irragionevole. C'è un modo più efficiente per farlo? Forse con uno script bash più complicato?

Una domanda secondaria è "perché il mio approccio attuale è così lento?"

+6

Il parametro exec genera un nuovo processo di catrame per ogni file trovato. Inoltre l'operazione di aggiornamento del tar è costosa. – theomega

+0

quanti file hai e quanto sono grandi? L'impatto del richiamo di tar per ciascuno dei file fa una grande differenza per 10 file di petabyte o 10 miliardi di piccoli file – sfussenegger

+0

@sfussenegger: 8 byte ciascuno ... hanno una domanda aggiornata. Come affermato in q, ci sono * milioni * di file. –

risposta

19

Se hai già fatto il secondo comando che creato l'elenco dei file, basta usare l'opzione -T per dire a tar di leggere i nomi dei file da quella lista di file salvata. L'esecuzione di 1 comando tar vs N comandi tar sarà molto meglio.

+0

Dopo aver eseguito 'xargs' per un po ', ho provato questo approccio ... ed era ** molto ** più veloce! –

+2

state attenti con 'xargs' in questa situazione: se ci sono molti nomi di file passati ad esso, esegue' tar' più volte su sottoinsiemi della lista dei file. Nel tuo caso, con 'tar -u' che probabilmente funziona, ma se stai creando un file tar' tar -c', solo l'ultimo sottoinsieme di file sarà lì una volta finito ... – drevicko

7

C'è xargs per questo:

find data/ -name filepattern-*2009* -print0 | xargs -0 tar uf 2009.tar 

Indovinare perché è lento è difficile come non ci sono molte informazioni. Qual è la struttura della directory, quale filesystem usi, come è stato configurato per la creazione. Avere milioni di file in una singola directory è una situazione abbastanza difficile per la maggior parte dei file system.

+0

La directory viene cancellata bene. ext3, btw. Come ho già detto, il comando find viene eseguito rapidamente, quindi credo che il file system, la struttura delle directory, ecc. Non sia un problema. –

+0

Penso che dovrai aggiungere '--max-args = n' (breve' -n n') dove 'n' è il numero massimo di argomenti che tar (o qualsiasi altro programma) può assumere. 'getconf ARG_MAX' dovrebbe mostrare quanto è alto questo limite (131.072 sulla mia macchina). È possibile però che xargs si prenda cura di questo stesso. – sfussenegger

+0

Wow! Così ho eseguito un altro comando con 'xargs' come hai detto 15 minuti fa, e il file tar risultante è già il 25% delle dimensioni del mio comando originale. Grazie. –

2

Il modo in cui al momento si dispone, si sta invocando il comando tar ogni volta che trova un file, che non è sorprendentemente lento. Invece di prendere le due ore per stampare più il tempo necessario per aprire l'archivio tar, vedere se i file non sono aggiornati e aggiungerli all'archivio, in realtà stai moltiplicando le volte. Potresti avere un successo migliore invocando il comando tar una volta, dopo aver raggruppato tutti i nomi, eventualmente usando xargs per ottenere l'invocazione. A proposito, spero che tu stia usando 'filepattern- * 2009 *' e non filepattern- * 2009 * poiché le stelle saranno espanse dalla shell senza virgolette.

24

Una possibilità è quella di utilizzare cpio per generare un archivio tar-formato:

$ find data/ -name "filepattern-*2009*" | cpio -ov --format=ustar > 2009.tar 

cpio funziona in modo nativo con un elenco di nomi di file da stdin, piuttosto che una directory di livello superiore, che rende è uno strumento ideale per questa situazione.

+2

questa è una soluzione elegante. e puoi eseguirlo su una rete. sostituisci '> 2009.tar' con' | ssh host tar xf -' –

+5

'trova dati/-print0 | tar -T - --null --create -f archive.tar' legge l'elenco di file da stdout, e usa il delimitatore di file null –

+3

Il mio tar di ubuntu non ama avere '--null' dopo' -T'. Ho dovuto usare: 'trova dati/-print0 | tar --null -T - --create -f archive.tar' –

8

Ecco una combinazione ritrovamento di catrame che può fare ciò che si vuole, senza l'uso di xargs o exec (che dovrebbe sfociare in un'accelerazione notevole):

tar --version # tar (GNU tar) 1.14 

# FreeBSD find (on Mac OS X) 
find -x data -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from - 

# for GNU find use -xdev instead of -x 
gfind data -xdev -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from - 

# added: set permissions via tar 
find -x data -name "filepattern-*2009*" -print0 | \ 
    tar --null --no-recursion --owner=... --group=... --mode=... -uf 2009.tar --files-from - 
3

Per gestire correttamente i nomi di file con caratteri bizzarri (ma legali) (come le nuove linee, ...) è necessario scrivere l'elenco dei file su filesOfInterest.txt utilizzando -print0 del ritrovamento:

find -x data -name "filepattern-*2009*" -print0 > filesOfInterest.txt 
tar --null --no-recursion -uf 2009.tar --files-from filesOfInterest.txt 
-2

più semplice (anche rimuovere il file dopo la creazione archivio):

find *.1 -exec tar czf '{}.tgz' '{}' --remove-files \; 
+1

Non c'è quasi nessuno differenza con l'approccio originale del richiedente, che secondo come riferito era troppo lento. Inoltre, rimuove inutilmente i file di origine, che non è stato richiesto e sarà sicuramente indesiderabile. –