Haga que xargs ejecute el comando una vez para cada línea de entrada

StackOverflow https://stackoverflow.com/questions/199266

  •  11-07-2019
  •  | 
  •  

Pregunta

¿Cómo puedo hacer que xargs ejecute el comando exactamente una vez para cada línea de entrada dada? Su comportamiento predeterminado es dividir las líneas y ejecutar el comando una vez, pasando varias líneas a cada instancia.

  

De http://en.wikipedia.org/wiki/Xargs :

     

buscar / ruta -tipo f -print0 | xargs -0 rm

     

En este ejemplo, find alimenta la entrada de xargs con una larga lista de nombres de archivos. xargs luego divide esta lista en sublistas y llama a rm una vez por cada sublista. Esto es más eficiente que esta versión funcionalmente equivalente:

     

find / path -type f -exec rm '{}' \;

Sé que find tiene el " exec " bandera. Solo estoy citando un ejemplo ilustrativo de otro recurso.

¿Fue útil?

Solución

Lo siguiente solo funcionará si no tiene espacios en su entrada:

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

desde la página del manual:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

Otros consejos

Me parece que todas las respuestas existentes en esta página son incorrectas, incluida la marcada como correcta. Esto se debe al hecho de que la pregunta está redactada de manera ambigua.

Resumen: & nbsp; Si desea ejecutar el comando " exactamente una vez para cada línea de entrada dada, " pasando toda la línea (sin la nueva línea) al comando como un argumento único , entonces esta es la mejor forma de hacerlo compatible con UNIX:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargs puede o no tener extensiones útiles que le permitan eliminar tr , pero no están disponibles en OS X y otros sistemas UNIX.

Ahora para la larga explicación & # 8230;


Hay dos problemas a tener en cuenta al usar xargs:

  1. ¿cómo divide la entrada en " argumentos " ;; y
  2. cuántos argumentos para pasar el comando secundario a la vez.

Para probar el comportamiento de xargs, necesitamos una utilidad que muestre cuántas veces se está ejecutando y con cuántos argumentos. No sé si hay una utilidad estándar para hacer eso, pero podemos codificarla con bastante facilidad en bash:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

Suponiendo que lo guarde como show en su directorio actual y lo haga ejecutable, así es como funciona:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

Ahora, si la pregunta original es realmente sobre el punto 2. anterior (como creo que es, después de leerlo varias veces) y debe leerse así (cambia en negrita):

  

¿Cómo puedo hacer que xargs ejecute el comando exactamente una vez para cada argumento de entrada dado? Su comportamiento predeterminado es dividir la entrada en argumentos y ejecutar el comando tan pocas veces como sea posible , pasando múltiples argumentos a cada instancia.

entonces la respuesta es -n 1 .

Comparemos el comportamiento predeterminado de xargs, que divide la entrada alrededor del espacio en blanco y llama al comando la menor cantidad de veces posible:

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

y su comportamiento con -n 1 :

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

Si, por otro lado, la pregunta original era sobre el punto 1. división de entrada y debía leerse así (muchas personas que vienen aquí parecen pensar que ese es el caso, o están confundiendo los dos problemas):

  

¿Cómo puedo hacer que xargs ejecute el comando con exactamente un argumento para cada línea de entrada dada? Su comportamiento predeterminado es dividir las líneas alrededor del espacio en blanco .

entonces la respuesta es más sutil.

Uno podría pensar que -L 1 podría ser de ayuda, pero resulta que no cambia el análisis de argumentos. Solo ejecuta el comando una vez para cada línea de entrada, con tantos argumentos como había en esa línea de entrada:

$ echo 

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

$ echo 

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

$ echo 

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

$ echo one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show -> "one " "two" "three and four"

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show -> "one " -> "two" -> "three and four" one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show -> "one " "two" "three and four"

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one \ntwo\nthree and four' | xargs -L 1 ./show -> "one" "two" -> "three" "and" "four"

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>one\ntwo\nthree and four' | xargs -L 1 ./show -> "one" -> "two" -> "three" "and" "four"

No solo eso, sino que si una línea termina con espacios en blanco, se agrega a la siguiente:

<*>

Claramente, -L no se trata de cambiar la forma en que xargs divide la entrada en argumentos.

El único argumento que lo hace de manera multiplataforma (excluyendo las extensiones GNU) es -0 , que divide la entrada alrededor de bytes NUL.

Entonces, solo es cuestión de traducir nuevas líneas a NUL con la ayuda de tr :

<*>

Ahora el análisis del argumento se ve bien, incluido el espacio en blanco al final.

Finalmente, si combina esta técnica con -n 1 , obtendrá exactamente una ejecución de comando por línea de entrada, cualquiera que sea la entrada que tenga, que puede ser otra forma de ver la pregunta original ( posiblemente el más intuitivo, dado el título):

<*>

Si desea ejecutar el comando para cada línea (es decir, resultado) que proviene de find , entonces ¿para qué necesita el xargs para ?

Prueba:

find ruta -type f -exec su-comando {} \;

donde el {} literal se sustituye por el nombre del archivo y se necesita el \; literal para que find sepa que el comando personalizado finaliza allí.

EDITAR:

(después de editar su pregunta aclarando que sabe acerca de -exec )

De man xargs :

  

-L líneas máximas
   Utilice como máximo max-lines líneas de entrada no en blanco por línea de comando. Trailing    los espacios en blanco hacen que una línea de entrada continúe lógicamente en la siguiente línea de entrada.    Implica -x.

Tenga en cuenta que los nombres de archivo que terminan en espacios en blanco le causarán problemas si utiliza xargs :

$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\  b c\  c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory

Entonces, si no le importa la opción -exec , es mejor que use -print0 y -0 :

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
  

¿Cómo puedo hacer que xargs ejecute el comando exactamente una vez para cada línea de entrada dada?

No uso la respuesta -L 1 porque no maneja archivos con espacios en ellos, que es una función clave de find -print0 .

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: space.txt: No such file or directory
ls: with: No such file or directory

Una mejor solución es usar tr para convertir nuevas líneas en caracteres nulos ( \ 0 ), y luego usar el argumento xargs -0 .

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

Si necesita limitar el número de llamadas, puede usar el argumento -n 1 para hacer una llamada al programa para cada entrada:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

Esto también le permite filtrar la salida de find antes convirtiendo los saltos en nulos.

find . -name \*.xml | grep -v /workspace/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

Otra alternativa ...

find /path -type f | while read ln; do echo "processing $ln"; done
find path -type f | xargs -L1 command 

es todo lo que necesitas.

¡Estas dos formas también funcionan, y funcionarán para otros comandos que no usan find!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

caso de uso de ejemplo:

find . -name "*.pyc" | xargs -i rm '{}

eliminará todos los archivos pyc de este directorio, incluso si los archivos pyc contienen espacios.

El siguiente comando encontrará todos los archivos (-tipo f) en / path y luego los copiará usando cp en la carpeta actual. Tenga en cuenta el uso if -I% para especificar un carácter de marcador de posición en la línea de comando cp para que los argumentos puedan colocarse después del nombre del archivo.

find / path -type f -print0 | xargs -0 -I% cp%.

Probado con xargs (GNU findutils) 4.4.0

Puede limitar el número de líneas o argumentos (si hay espacios entre cada argumento) usando las banderas --max-lines o --max-args, respectivamente.

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

Parece que no tengo suficiente reputación para agregar un comentario a la respuesta de Tobia arriba , así que estoy agregando esto " respuesta " para ayudar a aquellos de nosotros que queremos experimentar con xargs de la misma manera en las plataformas de Windows.

Aquí hay un archivo por lotes de Windows que hace lo mismo que el codificado rápidamente de Tobia "mostrar" guión:

@echo off
REM
REM  cool trick of using "set" to echo without new line
REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
    exit /b
)

<nul set /p=Args:  "%~1"
shift

:start
if not "%~1" == "" (
    <nul set /p=, "%~1"
    shift
    goto start
)
echo.

Las respuestas de @Draemon parecen ser correctas con " -0 " incluso con espacio en el archivo.

Estaba probando el comando xargs y descubrí que " -0 " funciona perfectamente con " -L " ;. incluso los espacios son tratados (si la entrada fue nula terminada). El siguiente es un ejemplo:

#touch "file with space"
#touch "file1"
#touch "file2"

Lo siguiente dividirá los valores nulos y ejecutará el comando en cada argumento de la lista:

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

entonces -L1 ejecutará el argumento en cada carácter terminado en nulo si se usa con " -0 " ;. Para ver la diferencia intente:

 #find . -name 'file*' -print0 | xargs -0 | xargs -L1
 ./file with space ./file1 ./file2

incluso esto se ejecutará una vez:

 #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
./file with space ./file1 ./file2

El comando se ejecutará una vez como " -L " ahora no se divide en byte nulo. debe proporcionar ambos " -0 " y '' -L '' trabajar.

En su ejemplo, el punto de canalizar la salida de find a xargs es que el comportamiento estándar de la opción find -exec es ejecutar el comando una vez para cada archivo encontrado. Si está utilizando find, y desea su comportamiento estándar, entonces la respuesta es simple: no use xargs para empezar.

ejecutar ant task clean-all en cada build.xml en la carpeta actual o subcarpeta.

find . -name 'build.xml' -exec ant -f {} clean-all \;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top