Faire en sorte que xargs exécute la commande une fois pour chaque ligne d'entrée

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

  •  11-07-2019
  •  | 
  •  

Question

Comment puis-je faire en sorte que xargs exécute la commande exactement une fois pour chaque ligne d'entrée donnée? Son comportement par défaut consiste à tronquer les lignes et à exécuter la commande une fois, en transmettant plusieurs lignes à chaque instance.

  

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

     

find / path -type f -print0 | xargs -0 rm

     

Dans cet exemple, find alimente l'entrée de xargs avec une longue liste de noms de fichiers. xargs divise ensuite cette liste en sous-listes et appelle rm une fois pour chaque sous-liste. C'est plus efficace que cette version fonctionnellement équivalente:

     

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

Je sais que la recherche a le "exec" drapeau. Je viens de citer un exemple illustratif tiré d'une autre ressource.

Était-ce utile?

La solution

Ce qui suit ne fonctionnera que s'il n'y a pas d'espaces dans votre entrée:

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

depuis la page de manuel:

-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.

Autres conseils

Il me semble que toutes les réponses existantes sur cette page sont erronées, y compris celle marquée comme correcte. Cela provient du fait que la question est formulée de manière ambiguë.

Résumé: & nbsp; Si vous souhaitez exécuter la commande "exactement une fois pour chaque ligne d'entrée indiquée", transmettez la ligne entière (sans la nouvelle ligne) à la commande sous la forme d'un seul argument , c’est la meilleure façon de le faire compatible avec UNIX:

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

GNU xargs peut éventuellement ne pas avoir d'extensions utiles vous permettant de supprimer tr , mais elles ne sont pas disponibles sur OS X ni sur d'autres systèmes UNIX.

Maintenant pour la longue explication…

Deux problèmes doivent être pris en compte lors de l'utilisation de xargs:

  1. comment divise-t-il l'entrée en "arguments" ;; et
  2. combien d'arguments pour passer la commande enfant à la fois.

Pour tester le comportement de xargs, nous avons besoin d'un utilitaire indiquant combien de fois il est exécuté et avec combien d'arguments. Je ne sais pas s’il existe un utilitaire standard pour le faire, mais nous pouvons le coder assez facilement en bash:

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

En supposant que vous enregistrez le fichier sous show dans votre répertoire actuel et que vous le rendiez exécutable, voici comment cela fonctionne:

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

Maintenant, si la question initiale concerne vraiment le point 2. ci-dessus (comme je le pense, après l'avoir relue plusieurs fois) et qu'il se lise comme suit (changements en gras):

  

Comment puis-je faire en sorte que xargs exécute la commande exactement une fois pour chaque argument de l'entrée donnée? Son comportement par défaut consiste à fragmenter l'entrée dans les arguments et à exécuter la commande aussi peu que possible , en transmettant plusieurs arguments à chaque instance.

alors la réponse est -n 1 .

Comparons le comportement par défaut de xargs, qui divise l'entrée en espaces et appelle la commande aussi souvent que possible:

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

et son comportement avec -n 1 :

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

Si, en revanche, la question initiale concernait le fractionnement des entrées du point 1. et devait être lue comme ceci (beaucoup de personnes qui arrivent ici semblent penser que c'est le cas ou confondent les deux problèmes):

  

Comment puis-je faire en sorte que xargs exécute la commande avec exactement un argument pour chaque ligne d'entrée indiquée? Son comportement par défaut consiste à découper les lignes autour des espaces .

alors la réponse est plus subtile.

On pourrait penser que -L 1 pourrait être utile, mais il s'avère que cela ne change pas l'analyse des arguments. Il n'exécute la commande qu'une seule fois pour chaque ligne d'entrée, avec autant d'arguments qu'il y en avait sur cette ligne d'entrée:

$ echo 

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

$ echo 

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

$ echo 

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

<*>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"

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

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

Non seulement cela, mais si une ligne se termine par un espace, elle est ajoutée à la suivante:

<*>

Clairement, -L ne consiste pas à changer la façon dont xargs divise l'entrée en arguments.

Le seul argument qui le fait de manière multiplateforme (à l'exception des extensions GNU) est -0 , qui divise l'entrée en octets NUL.

Ensuite, il suffit de traduire les nouvelles lignes en NUL à l'aide de tr :

<*>

Maintenant, l'analyse des arguments semble correcte, y compris les espaces finaux.

Enfin, si vous combinez cette technique avec -n 1 , vous obtenez exactement une commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être un autre moyen de regarder la question initiale ( peut-être le plus intuitif, étant donné le titre):

<*>

Si vous souhaitez exécuter la commande pour chaque ligne (résultat) provenant de find , vous avez besoin de quoi xargs ?

Essayez:

trouve chemin -type f -exec votre-commande {} \;

où le littéral {} est remplacé par le nom de fichier et le littéral \; est nécessaire pour que find sache que la commande personnalisée se termine là-bas.

EDIT:

(après la modification de votre question précisant que vous connaissez -exec )

À partir de man xargs :

  

-L max-lignes
   Utilisez au maximum max-lignes lignes d'entrée non vides par ligne de commande. Trailing    Les blancs font que la ligne d’entrée continue logiquement sur la ligne d’entrée suivante.    Implique -x.

Notez que les noms de fichiers qui se terminent par des blancs vous causeraient des problèmes si vous utilisez 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

Donc si vous ne vous souciez pas de l'option -exec , vous feriez mieux d'utiliser -print0 et -0 :

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

Comment puis-je faire en sorte que xargs exécute la commande exactement une fois pour chaque ligne d'entrée donnée?

Je n'utilise pas la réponse -L 1 car elle ne gère pas les fichiers contenant des espaces, ce qui est une fonction clé 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

Une meilleure solution consiste à utiliser tr pour convertir les nouvelles lignes en caractères nuls ( \ 0 ), puis utiliser l'argument xargs -0 . .

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

Si vous devez ensuite limiter le nombre d'appels, vous pouvez utiliser l'argument -n 1 pour passer un appel au programme pour chaque entrée:

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

Ceci vous permet également de filtrer la sortie de find avant de convertir les coupures en valeurs null.

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

Une autre alternative ...

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

est tout ce dont vous avez besoin.

Ces deux méthodes fonctionnent également et fonctionnent pour d'autres commandes qui n'utilisent pas find!

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

exemple d'utilisation:

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

supprimera tous les fichiers pyc de ce répertoire même si ceux-ci contiennent des espaces.

La commande suivante trouvera tous les fichiers (-type f) dans / chemin , puis les copiera à l'aide de cp dans le dossier en cours. Notez l'utilisation de -I% pour spécifier un caractère de substitution dans la ligne de commande cp afin que les arguments puissent être placés après le nom du fichier.

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

Testé avec xargs (GNU findutils) 4.4.0

Vous pouvez limiter le nombre de lignes ou d'arguments (s'il y a des espaces entre les arguments) en utilisant les options --max-lines ou --max-args, respectivement.

  -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.

Il semble que je n'ai pas assez de réputation pour ajouter un commentaire à la réponse de Tobia ci-dessus , aussi j'ajoute ceci " répondre " pour aider ceux d'entre nous qui souhaitent expérimenter xargs de la même manière sur les plates-formes Windows.

Voici un fichier de commandes Windows qui fait la même chose que "show" codé rapidement par Tobia. script:

@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.

@Draemon semble avoir raison avec "-0" même avec de l'espace dans le fichier.

J'essayais la commande xargs et j'ai trouvé que "-0" fonctionne parfaitement avec "-L". même les espaces sont traités (si l'entrée était terminée par un null). voici un exemple:

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

Ce qui suit divisera les valeurs NULL et exécutera la commande sur chaque argument de la liste:

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

so -L1 exécutera l'argument sur chaque caractère terminé par un caractère nul s'il est utilisé avec "-0". Pour voir la différence, essayez:

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

même cela sera exécuté une fois:

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

La commande sera exécutée une fois sous le " -L " maintenant ne se divise pas sur octet nul. vous devez fournir les deux " -0 " et "-L" travailler.

Dans votre exemple, l’intérêt de canaliser la sortie de find vers xargs est que le comportement standard de l’option -exec de find consiste à exécuter la commande une fois pour chaque fichier trouvé. Si vous utilisez find et voulez son comportement standard, la réponse est simple: n'utilisez pas xargs pour commencer.

exécute la tâche ant clean-all sur chaque build.xml du dossier actuel ou du sous-dossier.

find . -name 'build.xml' -exec ant -f {} clean-all \;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top