Come posso includere una pipe | nel mio comando linux trova -exec?


220

Questo non funziona. Questo può essere fatto in find? O ho bisogno di xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;

Risposte:


145

Il compito di interpretare il simbolo pipe come istruzione per eseguire più processi e reindirizzare l'output di un processo all'input di un altro processo è responsabilità della shell (/ bin / sh o equivalente).

Nel tuo esempio puoi scegliere di utilizzare la shell di livello superiore per eseguire le tubazioni in questo modo:

find -name 'file_*' -follow -type f -exec zcat {} \; | agrep -dEOE 'grep'

In termini di efficienza, questo risultato costa una chiamata di find, numerose chiamate di zcat e una chiamata di agrep.

Ciò comporterebbe la generazione di un solo processo agrep che elaborerebbe tutto l'output prodotto da numerose invocazioni di zcat.

Se per qualche motivo desideri invocare più volte, puoi fare:

find . -name 'file_*' -follow -type f \
    -printf "zcat %p | agrep -dEOE 'grep'\n" | sh

Questo costruisce un elenco di comandi che utilizzano i pipe per eseguire, quindi li invia a una nuova shell per essere effettivamente eseguiti. (L'omissione dell'ultimo "| sh" è un buon modo per eseguire il debug o eseguire serie di righe di comando a secco come questa.)

In termini di efficienza, questo risultato costa una invocazione di find, una invocazione di sh, numerose invocazioni di zcat e numerose invocazioni di agrep.

La soluzione più efficiente in termini di numero di invocazioni di comandi è il suggerimento di Paul Tomblin:

find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'

... che costa una invocazione di find, una invocazione di xargs, alcune invocazioni di zcat e una invocazione di agrep.


1
Un altro vantaggio di xargs sarebbe che puoi velocizzarlo con la moderna CPU multi core ancora di più, usando l'opzione -P (-P 0).
flolo,

Sì, lo sw -P è davvero un bel modo per accelerare l'esecuzione in generale. Sfortunatamente, si corre il rischio che l'output di processi zcat paralleli venga convogliato in interfogliati agrep, il che influirebbe sul risultato. Questo effetto può essere dimostrato usando: echo -e "1 \ n2" | xargs -P 0 -n 1 sì | uniq
Rolf W. Rasmussen,

@Adam, ho apportato la modifica suggerita.
Paul Tomblin,

per il quale è possibile installare lo splendido comando xjobs (originariamente da Solaris)
visto il

4
Una risposta più semplice e più generale è a stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Eric O Lebigot,

279

la soluzione è semplice: eseguire tramite sh

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;

17
Ciò che l'OP stava cercando di realizzare può essere soddisfatto con i suggerimenti di cui sopra, ma questo è quello che risponde effettivamente alla domanda posta. Ci sono ragioni per farlo in questo modo: exec è molto più potente del semplice operare sui file restituiti da find, specialmente se combinato con test. Ad esempio: find geda-gaf / -type d -exec bash -c 'DIR = {}; [[$ (trova $ DIR -maxdepth 1 | xargs grep -i spice | wc -l) -ge 5]] && echo $ DIR '\; Restituirà tutte le directory nel percorso di ricerca che contengono più di 5 righe in totale tra tutti i file in quella directory contenente la parola spezia
swarfrat,

3
Migliore risposta. Greppare l'intero output (come suggeriscono altre risposte) non è lo stesso di grep per ogni file. Suggerimento: invece di sh, puoi usare qualsiasi altra shell tu voglia (l'ho provato con bash e funziona bene).
pagliuca,

1
Assicurati di non trascurare l' -copzione. Altrimenti riceverai un No such file or directorymessaggio di errore sconcertante .
asmaier

ecco un ottimo sostituto ps che usa find con piping all'interno di una shell exec: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
parità

1
Esempio di ricerca di file e rinominazione di sed con l'espressione regolare find -type f -name '*.mdds' -exec sh -c "echo {} | sed -e 's/_[0-9]\+//g' | xargs mv {}" \;
Rostfrei

16
find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'

Sperando di evitare -print e xargs per motivi di efficienza. Forse è davvero un mio problema: find non è in grado di gestire i comandi inoltrati tramite -exec
someguy il

Questo non funziona con file con spazi nei loro nomi; riparare, sostituire -print con -print0 e aggiungere l'opzione -0 a xargs
Adam Rosenfield

2
@someguy - Wha? Evitare xargs per motivi di efficienza? Chiamare un'istanza di zcat e passargli un elenco di più file è molto più efficiente che eseguirne una nuova per ogni file trovato.
Sherm Pendley,

@Adam - Ho apportato la modifica suggerita. Il 99% delle volte quando sto trovando, è nelle mie directory del codice sorgente e nessuno dei file contiene spazi, quindi non mi preoccupo di print0. Ora la mia directory dei documenti, d'altra parte, ricordo print0.
Paul Tomblin,

10

È inoltre possibile reindirizzare a un whileciclo che può eseguire più azioni sul file che findindividua. Quindi eccone uno per cercare negli jararchivi un dato file di classe java nella cartella con una grande distribuzione di jarfile

find /usr/lib/eclipse/plugins -type f -name \*.jar | while read jar; do echo $jar; jar tf $jar | fgrep IObservableList ; done

il punto chiave è che il whileciclo contiene più comandi che fanno riferimento al nome del file passato separato da punto e virgola e questi comandi possono includere pipe. Quindi, in questo esempio, faccio eco al nome del file corrispondente, quindi elenco ciò che è nel filtro di archivio per un determinato nome di classe. L'output è simile a:

/usr/lib/eclipse/plugins/org.eclipse.core.contenttype.source_3.4.1.R35x_v20090826-0451.jar /usr/lib/eclipse/plugins/org.eclipse.core.databinding.observable_1.2.0.M20090902-0800 .jar org / eclipse / core / databinding / osservable / list / IObservableList .class /usr/lib/eclipse/plugins/org.eclipse.search.source_3.5.1.r351_v20090708-0800.jar / usr / lib / eclipse / plugins / org.eclipse.jdt.apt.core.source_3.3.202.R35x_v20091130-2300.jar /usr/lib/eclipse/plugins/org.eclipse.cvs.source_1.0.400.v201002111343.jar / usr / lib / eclipse / plugins / org.eclipse.help.appserver_3.1.400.v20090429_1800.jar

nella mia shell bash (xubuntu10.04 / xfce) rende davvero grassetto il nome di classe abbinato mentre fgrepevidenzia la stringa abbinata; questo rende veramente facile scansionare l'elenco di centinaia di jarfile che sono stati cercati e vedere facilmente eventuali corrispondenze.

su windows puoi fare la stessa cosa con:

for /R %j in (*.jar) do @echo %j & @jar tf %j | findstr IObservableList

nota che in Windows il comando separator è '&' not ';' e che '@' sopprime l'eco del comando per fornire un output ordinato proprio come linux trova l'output sopra; sebbene findstrnon sia in grassetto la stringa abbinata, quindi devi guardare un po 'più vicino all'output per vedere il nome della classe corrispondente. Si scopre che il comando 'for' di Windows conosce parecchi trucchi come scorrere ciclicamente i file di testo ...

godere


2

Ho scoperto che l'esecuzione di un comando shell shell (sh -c) funziona meglio, ad esempio:

find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;

0

Se stai cercando una semplice alternativa, puoi farlo usando un ciclo:

for i in $(find -name 'file_*' -follow -type f);do zcat $i | agrep -dEOE 'grep');done

o, forma più generale e di facile comprensione:

for i in $(YOUR_FIND_COMMAND);do YOUR_EXEC_COMMAND_AND_PIPES );done

e sostituisci {} con $ i in YOUR_EXEC_COMMAND_AND_PIPES

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.