Il me semble que toutes les réponses existantes sur cette page sont fausses, y compris celle marquée comme correcte. Cela découle du fait que la question est formulée de manière ambiguë.
Résumé: Si vous voulez exécuter la commande "exactement une fois pour chaque ligne d'entrée donnée", en passant la ligne entière (sans la nouvelle ligne) à la commande comme un seul argument, alors c'est la meilleure façon compatible UNIX de le faire:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
peut ou non avoir des extensions utiles qui vous permettent de vous en débarrasser tr
, mais elles ne sont pas disponibles sur OS X et d'autres systèmes UNIX.
Maintenant pour la longue explication…
Il y a deux problèmes à prendre en compte lors de l'utilisation de xargs:
- comment divise-t-il l'entrée en "arguments"; et
- le nombre d'arguments pour passer la commande enfant à la fois.
Pour tester le comportement de xargs, nous avons besoin d'un utilitaire qui montre 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 l'enregistrez comme 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 d'origine concerne vraiment le point 2 ci-dessus (comme je pense que c'est le cas, après l'avoir lu plusieurs fois) et qu'elle doit être lue comme ceci (changements en gras):
Comment puis-je faire en sorte que xargs exécute la commande exactement une fois pour chaque argument d'entrée donné? Son comportement par défaut consiste à fragmenter l' entrée en arguments et à exécuter la commande le moins de fois possible , en passant plusieurs arguments à chaque instance.
alors la réponse est -n 1
.
Comparons le comportement par défaut de xargs, qui divise l'entrée autour des espaces et appelle la commande aussi peu de fois 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, d'un autre côté, la question initiale portait sur le point 1, le fractionnement des entrées et qu'il devait être lu comme ceci (beaucoup de gens qui viennent ici semblent penser que c'est le cas, ou confondent les deux problèmes):
Comment puis-je faire exécuter xargs la commande avec exactement un argument pour chaque ligne d'entrée donné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 cela -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 $'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:
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
De toute évidence, il -L
ne s'agit pas de changer la façon dont xargs divise l'entrée en arguments.
Le seul argument qui le fait de manière multiplateforme (à l'exclusion 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 $'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 de fin.
Enfin, si vous combinez cette technique avec -n 1
, vous obtenez exactement une exécution de commande par ligne d'entrée, quelle que soit l'entrée que vous avez, ce qui peut être une autre façon de regarder la question d'origine (peut-être la plus intuitive, compte tenu du titre):
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
find /path -type f -delete
serait encore plus efficace :)