web-dev-qa-db-fra.com

Quand faut-il xargs?

La commande xargs me confond toujours. Y a-t-il une règle générale pour cela?

Considérez les deux exemples ci-dessous:

$ \ls | grep Cases | less

imprime les fichiers qui correspondent à 'Cases', mais changer la commande en touch nécessitera xargs:

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

$ \ls | grep Cases | xargs touch
136
Zaid

La différence réside dans les données que le programme cible accepte.

Si vous utilisez simplement un canal, il reçoit des données sur STDIN (le flux d'entrée standard) sous forme de pile de données brutes qu'il peut trier sur une ligne à la fois. Cependant, certains programmes n'acceptent pas leurs commandes en standard dans, ils s'attendent à ce que cela soit précisé dans les arguments de la commande. Par exemple touch prend un nom de fichier comme paramètre sur la ligne de commande comme ceci: touch file1.txt.

Si vous avez un programme qui génère des noms de fichiers sur sortie standard et que vous souhaitez les utiliser comme arguments à touch, vous devez utiliser xargs qui lit les données du flux STDIN et convertit chaque ligne en arguments séparés par des espaces dans la commande.

Ces deux choses sont équivalentes:

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

N'utilisez pas xargs sauf si vous savez exactement ce qu'il fait et pourquoi vous en avez besoin. Il est assez fréquent qu'il y ait une meilleure façon de faire le travail que d'utiliser xargs pour forcer la conversion. Le processus de conversion est également semé d'embûches potentielles comme l'évasion et l'expansion de Word, etc.

146
Caleb

Pour développer les réponses déjà fournies, xargs peut faire une chose intéressante qui devient de plus en plus importante dans le paysage informatique multicœur et distribué d'aujourd'hui: il peut traiter des tâches en parallèle.

Par exemple:

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

va encoder * .wav => * .flac, en utilisant trois processus à la fois (-P 3).

73
amphetamachine

xargs est particulièrement utile lorsque vous avez une liste de chemins de fichiers sur stdin et que vous voulez en faire quelque chose. Par exemple:

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

Examinons cette étape par étape:

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

En d'autres termes, notre entrée est une liste de chemins vers lesquels nous voulons faire quelque chose.

Pour savoir ce que fait xargs avec ces chemins, une astuce intéressante est d'ajouter echo avant votre commande, comme ceci:

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

L'argument -n 1 Obligera les xargs à transformer chaque ligne en une commande à part entière. La commande sed -i "s/color/colour/g" Remplacera toutes les occurrences de color par colour pour le fichier spécifié.

Notez que cela ne fonctionne que si vous n'avez pas d'espace sur vos chemins. Si vous le faites, vous devez utiliser des chemins terminés par des valeurs nulles comme entrée pour xargs en passant l'indicateur -0. Un exemple d'utilisation serait:

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

Ce qui fait la même chose que ce que nous avons décrit ci-dessus, mais fonctionne également si l'un des chemins contient un espace.

Cela fonctionne avec n'importe quelle commande qui produit des noms de fichiers en sortie tels que find ou locate. Si vous l'utilisez dans un référentiel git avec beaucoup de fichiers, il pourrait être plus efficace de l'utiliser avec git grep -l Au lieu de git ls-files, Comme ceci:

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

La commande git grep -l "color" "*.tex" Donnera une liste de fichiers "* .tex" contenant l'expression "color".

24
Sverre Rabbelier

Votre premier argument illustre assez bien la différence.

\ls | grep Cases | less vous permet de parcourir la liste des noms de fichiers produits par ls et grep. Peu importe qu'il s'agisse de noms de fichiers, ce ne sont que du texte.

\ls | grep Cases | xargs less vous permet de parcourir les fichiers dont les noms sont produits par la première partie de la commande. xargs prend une liste de noms de fichiers en entrée et une commande sur sa ligne de commande, et exécute la commande avec les noms de fichiers sur sa ligne de commande.

Lorsque vous envisagez d'utiliser xargs, gardez à l'esprit qu'il attend une entrée formatée d'une manière étrange: délimitée par des espaces, avec \, ' et " utilisé pour la citation (d'une manière inhabituelle, car \ n'est pas spécial entre guillemets). N'utilisez xargs que si vos noms de fichiers ne contiennent pas d'espace ou \'".

Dans votre exemple, vous n'avez pas du tout besoin d'utiliser xargs car find fera exactement et en toute sécurité ce que vous voulez faire.

Ce que vous voulez exactement utiliser find est:

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

Dans cet exemple, -maxdepth 1 Signifie uniquement rechercher dans le répertoire courant, ne descendez dans aucun sous-répertoire; par défaut, find apparaîtra dans tous les sous-répertoires (ce qui est souvent ce que vous voulez), sauf si vous le contraignez avec maxdepth. Le {} Est le nom du fichier qui sera remplacé à sa place et le + Est l'un des deux marqueurs de fin de commande, l'autre étant ;. La différence entre eux est que ; Signifie exécuter la commande sur chaque fichier un par un, tandis que + Signifie exécuter la commande sur tous les fichiers à la fois. Notez cependant que votre Shell essaiera probablement d'interpréter ; Lui-même, vous devrez donc l'échapper avec \; Ou ';'. Oui, find a un certain nombre de petits désagréments comme celui-ci, mais sa puissance le compense largement.

find et xargs sont difficiles à apprendre au début. Pour vous aider à apprendre xargs essayez d'utiliser l'option -p Ou --interactive Qui vous montrera la commande qu'elle est sur le point d'exécuter et vous demandera si vous souhaitez l'exécuter.

De même, avec find, vous pouvez utiliser -ok À la place de -exec Pour vous demander si vous souhaitez ou non exécuter la commande.

Il arrive cependant que find ne puisse pas faire tout ce que vous voulez et c'est là que xargs entre en jeu. La commande -exec N'acceptera qu'une seule instance de {} Apparaît, donc si vous obtenez une erreur avec find -type f -exec cp {} {}.bak \; Vous pouvez donc le faire comme ceci: find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Vous pouvez en savoir plus sur Exécuter les commandes dans le manuel GNU Findutils .

J'ai également mentionné que find fait ce que vous voulez en toute sécurité car lorsque vous traitez des fichiers, vous allez rencontrer des espaces et d'autres caractères qui causeront des problèmes avec xargs sauf si vous utilisez le -0 Ou --null Avec quelque chose qui génère des éléments d'entrée terminés par un caractère nul au lieu d'un espace.

4
aculich

xargs (avec find, sort, du, uniq, Perl et quelques autres) accepte un commutateur de ligne de commande pour dire "STDIN a une liste de fichiers, séparés par un octet NUL (0x00)". Cela facilite la gestion des noms de fichiers contenant des espaces et d'autres personnages amusants. Les noms de fichiers ne contiennent pas de NUL.

1
waltinator