¿Cuándo se necesitan los xargs?

134

El xargscomando siempre me confunde. ¿Hay una regla general para ello?

Considere los dos ejemplos a continuación:

$ \ls | grep Cases | less

imprime los archivos que coinciden con 'Casos', pero cambiar el comando a touchrequerirá xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch
Zaid
fuente

Respuestas:

143

La diferencia está en qué datos está aceptando el programa de destino.

Si solo usa una tubería, recibe datos en STDIN (el flujo de entrada estándar) como una pila de datos sin procesar que puede ordenar a través de una línea a la vez. Sin embargo, algunos programas no aceptan sus comandos de forma estándar, esperan que se especifique en los argumentos del comando. Por ejemplo touchtoma un nombre de archivo como un parámetro en la línea de comandos, así: touch file1.txt.

Si usted tiene un programa que da salida a los nombres de archivo de salida estándar y desea utilizarlas como argumentos a touch, usted tiene que utilizar xargsel que lee los datos de la secuencia STDIN y convierte cada línea en el espacio separadas argumentos para el comando.

Estas dos cosas son equivalentes:

# touch file1.txt
# echo file1.txt | xargs touch

No lo use a xargsmenos que sepa exactamente lo que está haciendo y por qué es necesario. Es frecuente que haya una mejor manera de hacer el trabajo que usar xargspara forzar la conversión. El proceso de conversión también está plagado de posibles dificultades como el escape y la expansión de palabras, etc.

Caleb
fuente
2
La advertencia me parece una pequeña cuerda. De las dos opciones comunes para obtener una secuencia en una línea de comando ( xargsy $(...)), xargs es mucho más seguro que la sustitución de comandos. Y no recuerdo haber encontrado un nombre de archivo legítimo con una nueva línea. ¿No son los problemas de escape y expansión de palabras problemas con la sustitución de comandos, no xargs?
Camh
66
@camh: Son posibles dificultades con ambos. En el shell, debe preocuparse de que los nombres de archivo se dividan en espacios, pestañas y líneas nuevas. En xargs, solo debes preocuparte por las nuevas líneas. En xargs, si su salida está formateada correctamente, puede dividir palabras / nombres de archivo en el carácter NUL en su lugar ( xargs -0), lo cual es útil junto con find -print0.
Ken Bloom
¿ xargsLlama al programa a través del shell con argumentos separados por espacios, o realmente construye la lista de argumentos internamente (por ejemplo, para usar con execv/ execp)?
detly
1
Lo construye internamente y usa execvp, por lo que es seguro. Además, GNU xargs (como se usa en Linux y algunos otros) le permite especificar la nueva línea como su delimitador -d \n, aunque BSD xargs (OSX et al) no parece admitir esta opción.
esponjoso
72

Para ampliar las respuestas ya proporcionadas, xargspuede hacer una cosa interesante que se está volviendo cada vez más importante en el panorama informático distribuido y multinúcleo de hoy: puede paralelar procesos de trabajo.

Por ejemplo:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

codificará * .wav => * .flac, utilizando tres procesos a la vez ( -P 3).

anfetamaquina
fuente
Guau. Debí haberlo sabido hace una semana cuando estaba haciendo exactamente lo mismo (excepto usar OGG) con 50GiB de WAV. :)
Alois Mahdal
¿por qué no usar el parámetro -exec que find tiene?
Evgeny
3
@Evgeny El -execparámetro no procesará trabajos en paralelo.
anfetamáquinas
Es bueno tener en cuenta que el -0argumentoxargs hace que considere que el NULLcarácter es el delimitador del elemento de entrada. find -print0salida de elementos delimitados por NULL. Esta es una gran práctica para los nombres de archivo que pueden contener espacios, comillas u otros caracteres especiales.
Dan Dascalescu
24

xargs es particularmente útil cuando tienes una lista de rutas de archivos en stdin y quieres hacer algo con ellas. Por ejemplo:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Examinemos esto paso a paso:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

En otras palabras, nuestra entrada es una lista de caminos en los que queremos hacer algo.

Para saber qué hace xargs con estas rutas, un buen truco es agregar echoantes de su comando, así:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

El -n 1argumento hará que xargs convierta cada línea en un comando propio. El sed -i "s/color/colour/g"comando reemplazará todas las apariciones de colorcon colourpara el archivo especificado.

Tenga en cuenta que esto solo funciona si no tiene espacios en sus caminos. Si lo hace, debe usar rutas terminadas nulas como entrada a xargs pasando la -0bandera. Un ejemplo de uso sería:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Lo que hace lo mismo que describimos anteriormente, pero también funciona si uno de los caminos tiene un espacio.

Esto funciona con cualquier comando que produce nombres de archivo como salida como findo locate. Sin embargo, si lo usa en un repositorio de git con muchos archivos, podría ser más eficiente usarlo en git grep -llugar de git ls-files, de esta manera:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

El git grep -l "color" "*.tex"comando dará una lista de archivos "* .tex" que contienen la frase "color".

Sverre Rabbelier
fuente
1
Es cierto, pero si has aprendido esto, también deberías aprender ¿Por qué es un bucle sobre la mala práctica de salida de find?
Comodín el
6

Su primer argumento ilustra la diferencia bastante bien.

\ls | grep Cases | lessle permite explorar la lista de nombres de archivos producidos por lsy grep. No importa que sean nombres de archivo, solo son texto.

\ls | grep Cases | xargs lessle permite examinar los archivos cuyos nombres son producidos por la primera parte del comando. xargstoma una lista de nombres de archivo como entrada y un comando en su línea de comando, y ejecuta el comando con los nombres de archivo en su línea de comando.

Al considerar el uso xargs, tenga en cuenta que se espera de entrada con formato de una manera extraña: por espacios en blanco delimitado, con \, 'y la "utiliza para citar (de una manera inusual, porque \no es cotizaciones en el interior especiales). Solo use xargssi sus nombres de archivo no contienen espacios en blanco o \'".

Gilles
fuente
@Gilles: xargs tiene la -0, --nullopción de solucionar el problema de los espacios (es muy probable que lo haya aprendido de usted :), por lo que supongo que se refiere a una xargllamada sin opciones , pero me desconcierta su referencia a las citas. ¿Tienes un enlace o un ejemplo al respecto? .. (ps. | xargs lesses un práctico "truco" +1 .. gracias ..
Peter.O
4

En su ejemplo, no necesita usar xargsnada, ya findque hará exactamente y con seguridad lo que desea hacer.

Exactamente lo que quieres usar findes:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

En este ejemplo -maxdepth 1, solo busca en el directorio actual, no descienda a ningún subdirectorio; de forma predeterminada, find buscará en todos los subdirectorios (que a menudo es lo que desea) a menos que lo restrinja con maxdepth. El {}es el nombre del archivo que será sustituido en su lugar y +es uno de los dos marcadores de fin de comando, el otro es ;. La diferencia entre ellos es que ;significa ejecutar el comando en cada archivo de uno en uno, mientras que +significa ejecutar el comando en todos los archivos a la vez. Tenga en cuenta, sin embargo, que su cáscara es probable que tratar de interpretar ;en sí, por lo que tendrá que escapar de ella, ya sea con \;o ';'. Sí, findtiene una serie de pequeñas molestias como esta, pero su poder lo compensa con creces.

Ambos findy xargsson difíciles de aprender al principio. Para ayudarlo a aprender, xargsintente usar la opción -po --interactiveque le mostrará el comando que está a punto de ejecutar y le preguntará si desea ejecutarlo o no.

De manera similar find, puede usar -oken lugar de -execpara preguntarle si desea ejecutar el comando o no.

Sin embargo, hay momentos en los findque no podrás hacer todo lo que quieras y ahí es donde xargsentra. El -execcomando solo aceptará una instancia de {}aparición, por lo que si recibes un error, find -type f -exec cp {} {}.bak \;puedes hacerlo así. :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Puede obtener más información sobre los comandos de ejecución en el manual de GNU Findutils .

Además, mencioné que con findseguridad hace lo que desea porque cuando se trata de archivos, encontrará espacios y otros caracteres que causarán problemas a xargsmenos que use la opción -0o --nulljunto con algo que genere elementos de entrada terminados por un carácter nulo. de espacios en blanco.

aculich
fuente
Los nombres de archivo @Wildcard con espacios o caracteres como 'o "pueden ser problemáticos, mientras findque manejarán esos casos sin problemas.
aculich
Sí, lo sé. Vea mi respuesta a la pregunta vinculada . Probablemente debería haber reformulado esa pregunta a una declaración en el comentario anterior, o haber agregado la frase "Ver la pregunta ..." delante de ella. : D
Comodín el
1

xargs(junto con find, sort, du, uniq, perly algunos otros) acepta un modificador de línea de comandos para decir "STDIN tiene una lista de archivos, separadas por un NUL (0x00) de bytes". Esto facilita el manejo de nombres de archivos con espacios y otros personajes divertidos en ellos. Los nombres de archivo no contienen NUL.

Waltinator
fuente
2
Creo que te refieres a "los nombres de archivo no pueden contener nulos".
anfetamáquina