¿Cómo incluyo una tubería | en mi comando linux find -exec?

220

Esto no esta funcionando. ¿Se puede hacer esto en find? ¿O necesito xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;
algún chico
fuente

Respuestas:

145

El trabajo de interpretar el símbolo de tubería como una instrucción para ejecutar múltiples procesos y canalizar la salida de un proceso a la entrada de otro proceso es responsabilidad del shell (/ bin / sh o equivalente).

En su ejemplo, puede elegir usar su caparazón de nivel superior para realizar la tubería de la siguiente manera:

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

En términos de eficiencia, estos resultados cuestan una invocación de búsqueda, numerosas invocaciones de zcat y una invocación de agrep.

Esto daría como resultado un solo proceso de concordancia que procesaría toda la salida producida por numerosas invocaciones de zcat.

Si por alguna razón desea invocar agrep varias veces, puede hacer lo siguiente:

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

Esto construye una lista de comandos usando tuberías para ejecutar, luego los envía a un nuevo shell para que realmente se ejecuten. (Omitir el "| sh" final es una buena manera de depurar o ejecutar ejecuciones secas de líneas de comando como esta).

En términos de eficiencia, este resultado cuesta una invocación de búsqueda, una invocación de sh, numerosas invocaciones de zcat y numerosas invocaciones de agrep.

La solución más eficiente en términos de número de invocaciones de comandos es la sugerencia de Paul Tomblin:

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

... que cuesta una invocación de búsqueda, una invocación de xargs, algunas invocaciones de zcat y una invocación de agrep.

Rolf W. Rasmussen
fuente
1
Otra ventaja de xargs sería que puede acelerarlo con una CPU moderna de múltiples núcleos aún más, utilizando el interruptor -P (-P 0).
flolo
Sí, el -P swich es una buena manera de acelerar la ejecución en general. Desafortunadamente, corre el riesgo de que la salida de procesos zcat paralelos se canalice a agrep intercalado, lo que afectaría el resultado. Este efecto se puede demostrar usando: echo -e "1 \ n2" | xargs -P 0 -n 1 sí | uniq
Rolf W. Rasmussen
@ Adam, hice el cambio sugerido.
Paul Tomblin
para lo cual puede instalar el espléndido comando xjobs (originalmente de Solaris)
sehe
44
Una respuesta más simple y más general es en stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Eric O Lebigot
279

la solución es fácil: ejecutar mediante sh

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
flolo
fuente
17
Lo que el OP estaba tratando de lograr se puede cumplir con las sugerencias anteriores, pero esta es la que realmente responde a la pregunta formulada. Hay razones para hacerlo de esta manera: exec es mucho más poderoso que simplemente operar en los archivos devueltos por find, especialmente cuando se combina con test. Por ejemplo: encuentre geda-gaf / -type d -exec bash -c 'DIR = {}; [[$ (find $ DIR -maxdepth 1 | xargs grep -i spice | wc -l) -ge 5]] && echo $ DIR '\; Devolverá todos los directorios en la ruta de búsqueda que contengan más de 5 líneas en total entre todos los archivos en ese directorio que contengan la palabra spice
swarfrat
3
La mejor respuesta. Agrupar toda la salida (como sugieren otras respuestas) no es lo mismo que grep cada archivo. Consejo: en lugar de sh, puede usar cualquier otro shell que desee (lo intenté con bash y funciona bien).
pagliuca el
1
Asegúrese de no pasar por alto la -copción. De lo contrario, recibirá un No such file or directorymensaje de error desconcertante .
asmaier
aquí hay un gran reemplazo de ps que utiliza find con tubería dentro de un shell ejecutado: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
parity3
1
Ejemplo de encontrar archivos y renombrarlos con sed usando expresión regular 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'
Paul Tomblin
fuente
Con la esperanza de evitar -print y xargs por razones de eficiencia. Tal vez ese sea realmente mi problema: find no puede manejar los comandos canalizados a través de -exec
someguy
Esto no funciona con archivos con espacios en sus nombres; para arreglar, reemplazar -print con -print0 y agregar la opción -0 a xargs
Adam Rosenfield
2
@someguy - ¿Qué? ¿Evitar los xargs por razones de eficiencia? Llamar a una instancia de zcat y pasarle una lista de varios archivos es mucho más eficiente que ejecutar una nueva instancia de cada archivo encontrado.
Sherm Pendley el
@ Adam - He hecho tu cambio sugerido. El 99% del tiempo cuando hago búsquedas, está en mis directorios de código fuente, y ninguno de los archivos tiene espacios, así que no me molesto con print0. Ahora mi directorio de documentos, por otro lado, recuerdo el print0.
Paul Tomblin
10

También puede canalizar a un whilebucle que puede realizar múltiples acciones en el archivo que findlocaliza. Así que aquí hay uno para buscar en los jararchivos un archivo de clase java dado en una carpeta con una gran distribución de jararchivos

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

el punto clave es que el while bucle contiene múltiples comandos que hacen referencia al nombre del archivo pasado separado por punto y coma y estos comandos pueden incluir tuberías. Entonces, en ese ejemplo, hago eco del nombre del archivo coincidente y luego enumero lo que está en el filtro de archivo para un nombre de clase dado. El resultado se ve así:

/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 / observable / 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

en mi shell bash (xubuntu10.04 / xfce) realmente hace que el nombre de clase coincidente sea en negrita cuando fgrepresalta la cadena coincidente; esto hace que sea realmente fácil escanear la lista de cientos de jararchivos que se buscaron y ver fácilmente cualquier coincidencia.

en windows puedes hacer lo mismo con:

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

tenga en cuenta que en Windows el separador de comandos es '&' no ';' y que la '@' suprime el eco del comando para dar una salida ordenada al igual que la salida de búsqueda de Linux anterior; aunque findstrno está en negrita la cadena coincidente, por lo que debe mirar un poco más cerca de la salida para ver el nombre de la clase coincidente. Resulta que el comando 'for' de Windows conoce bastantes trucos, como recorrer los archivos de texto ...

disfrutar

simbo1905
fuente
2

Descubrí que ejecutar un comando de shell de cadena (sh -c) funciona mejor, por ejemplo:

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

Si está buscando una alternativa simple, esto se puede hacer usando un bucle:

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

o, de forma más general y fácil de entender:

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

y reemplace cualquier {} por $ i en YOUR_EXEC_COMMAND_AND_PIPES

Louis Gagnon
fuente