Conozco el grep
comando y estoy aprendiendo acerca de las funcionalidades de xargs
, así que leí esta página que ofrece algunos ejemplos sobre cómo usar el xargs
comando.
Estoy confundido con el último ejemplo, el ejemplo 10. Dice "El comando xargs ejecuta el comando grep para encontrar todos los archivos (entre los archivos proporcionados por el comando find) que contenían una cadena 'stdlib.h'"
$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...
Sin embargo, ¿cuál es la diferencia de simplemente usar
$ find . -name '*.c' | grep 'stdlib.h'
?
Obviamente, todavía estoy luchando con lo que hace exactamente xargs, por lo que cualquier ayuda es apreciada.
command-line
grep
xargs
Alpha Omega
fuente
fuente
Respuestas:
Esto canaliza la salida (stdout) * de
find
a (stdin of) *grep 'stdlib.h'
como texto (es decir, los nombres de archivo se tratan como texto).grep
hace lo habitual y encuentra las líneas coincidentes en este texto (cualquier nombre de archivo que contenga el patrón). El contenido de los archivos nunca se lee.Esto construye un comando
grep 'stdlib.h'
para el que cada resultadofind
es un argumento, por lo que buscará coincidencias dentro de cada archivo encontrado porfind
(xargs
se puede considerar que convierte su stdin en argumentos para los comandos dados) *Úselo
-type f
en su comando de búsqueda, o obtendrá errores degrep
directorios coincidentes. Además, si los nombres de archivo tienen espacios,xargs
se estropearán mal, así que use el separador nulo agregando-print0
yxargs -0
para obtener resultados más confiables:* agregó estos puntos explicativos adicionales como se sugiere en el comentario de @cat
fuente
|
canaliza stdout al stdin de grep, que no es lo mismo que los argumentos de grep y da resultados confusos.find -name '*.c' -exec grep stdlib.h {} +
. Casi nunca uso xargs. También me sorprendió que nadie mencionara que xargs tiene un propósito similar para lagrep $(find)
sustitución de comandos, así que escribí una respuesta propia. Explicar los xargs como sustitución de comandos con menos limitaciones y problemas parece natural.find -delete
para ese caso especial? O para los comandos que no seanrm
, si tiene GNU find, se-exec some_command {} +
agrupa en lotes como xargs, en lugar del\;
comportamiento de ejecutar el comando por separado para cada uno.find
ejecuta el comando en cada archivo si y solo si usa-exec command \;
Bothxargs
y-exec command \+
llamará al comando con el número máximo de argumentos permitidos por el sistema. En otras palabras, son equivalentesxargs toma su entrada estándar y la convierte en argumentos de línea de comando.
find . -name '*.c' | xargs grep 'stdlib.h'
es muy similar aY dará los mismos resultados siempre que la lista de nombres de archivo no sea demasiado larga para una sola línea de comando. (Linux admite megabytes de texto en una sola línea de comando, por lo que generalmente no necesita xargs).
Pero ambos apestan, porque se rompen si sus nombres de archivo contienen espacios . En cambio,
find -print0 | xargs -0
funciona, pero también lo haceEso nunca canaliza los nombres de archivo en ningún lugar: los
find
agrupa en una gran línea de comando y se ejecutagrep
directamente.\;
en lugar de+
ejecuta grep por separado para cada archivo, que es mucho más lento. No hagas eso. Pero+
es una extensión GNU, por lo que debexargs
hacerlo de manera eficiente si no puede asumir que GNU encuentra.Si lo omite
xargs
,find | grep
su patrón coincide con la lista de nombres de archivo que sefind
imprime.Entonces, en ese punto, bien podrías hacerlo
find -name stdlib.h
. Por supuesto, con-name '*.c' -name stdlib.h
, no obtendrá ningún resultado porque esos patrones no pueden coincidir, y el comportamiento predeterminado de find es Y las reglas juntas.Sustituya
less
en cualquier punto del proceso para ver qué salida produce cualquier parte de la tubería.Lectura adicional: http://mywiki.wooledge.org/BashFAQ tiene algunas cosas geniales.
fuente
-d
que establecer el separador, por lo que puede usar-d'\n'
para manejar una lista separada por una nueva línea, lo que puede ser útil si maneja una lista de nombres de archivo en un archivo, etc. (siempre que los nombres de archivo no tengan nuevas líneas en ellos, eso es.)myfunc(){ local IFS=$'\n'; fgrep
stdlib.h` $ (find); }
también funciona con el mismo efecto. O como una línea, una(IFS=...; cmd...)
subshell también funciona para contener el cambio a IFS sin tener que guardarlo / restaurarlo.command $( find )
tipo de cosas. Los nombres de archivo problemáticos con espacios y caracteres especiales pueden romper este tipo de cosas. Como mínimo, comillas dobles la sustitución del comando.IFS
por lo que es equivalente a usarxargs '-d\n'
. La expansión global y el procesamiento de metacaracteres de shell ocurren antes de los efectos de sustitución de comandos, por lo que creo que es seguro incluso con nombres de archivo que contienen$()
o>
. Convino en que el uso de la división de palabras en la sustitución de comandos no es una buena práctica, excepto para el uso interactivo único en el que se sabe algo sobre los nombres de archivo. Perocommand "$(find)"
solo es útil si espera que produzca exactamente 1 nombre de archivo ...En general,
xargs
se usa para casos en los que canalizaría (con el símbolo|
) algo de un comando a otro (Command1 | Command2
), pero la salida del primer comando no se recibe correctamente como entrada para el segundo comando.Esto suele suceder cuando el segundo comando no maneja la entrada de datos a través de la entrada estándar (stdin) correctamente (por ejemplo: varias líneas como entrada, la forma en que se configuran las líneas, los caracteres utilizados como entrada, múltiples parámetros como entrada, el tipo de datos recibido como entrada, etc.). Para darle un ejemplo rápido, pruebe lo siguiente:
Ejemplo 1:
ls | echo
- Esto no hará nada yaecho
que no sabe cómo manejar la entrada que está recibiendo. Ahora, en este caso, si lo usamosxargs
, procesará la entrada de una manera que pueda ser manejada correctamenteecho
( por ejemplo: como una sola línea de información)ls | xargs echo
- Esto generará toda la información dels
una sola líneaEjemplo 2
Digamos que tengo varios archivos goLang dentro de una carpeta llamada go. Los buscaría con algo como esto:
find go -name *.go -type f | echo
- Pero si el símbolo de la tubería allí yecho
al final, no funcionaría.find go -name *.go -type f | xargs echo
- Aquí funcionaría gracias a,xargs
pero si quisiera cada respuesta delfind
comando en una sola línea, haría lo siguiente:find go -name *.go -type f | xargs -0 echo
- En este caso,find
se mostrará la misma salida deecho
.Los comandos como
cp, echo, rm, less
y otros que necesitan una mejor manera de manejar la entrada obtienen un beneficio cuando se usan conxargs
.fuente
xargs
se usa para generar automáticamente argumentos de línea de comandos basados (generalmente) en una lista de archivos.Entonces, considerando algunas alternativas al uso del
xargs
comando followoing :Hay varias razones para usarlo en lugar de otras opciones que no se mencionaron originalmente en otras respuestas:
find . -name '*.c' -exec grep 'stdlib.h' {}\;
generará ungrep
proceso para cada archivo; esto generalmente se considera una mala práctica y puede poner una gran carga en el sistema si se encuentran muchos archivos.grep 'stdlib.h' $(find . -name '*.c')
es probable que falle un comando, porque la salida de la$(...)
operación excederá la longitud máxima de la línea de comando del shellComo se mencionó en otras respuestas, la razón para usar el
-print0
argumento tofind
en este escenario y el-0
argumento para xargs es que los nombres de archivo con ciertos caracteres (por ejemplo, comillas, espacios o incluso líneas nuevas) aún se manejan correctamente.fuente