Comment utiliser la commande xargs sous Linux



Vous souhaitez exécuter une série de commandes Linux, mais l’une d’elles ne prend pas en charge les entrées de type pipe ? `xargs` peut récupérer la sortie d’une commande et l’utiliser comme paramètres pour une autre.

Tous les utilitaires standard de Linux sont associés à trois flux de données. Il s’agit du flux d’entrée standard (stdin), du flux de sortie standard (stdout) et du flux d’erreur standard (stderr).

Ces flux fonctionnent avec du texte. Nous envoyons une entrée (stdin) à une commande en utilisant du texte, et la réponse (stdout) est affichée dans la fenêtre du terminal sous forme de texte. Les messages d’erreur (stderr) sont également affichés en texte dans la même fenêtre.

L’une des fonctionnalités essentielles des systèmes d’exploitation de type Linux et Unix est la capacité à rediriger la sortie stdout d’une commande vers l’entrée stdin d’une autre. La première commande n’est pas affectée par le fait que sa sortie ne va pas vers une fenêtre de terminal, et la deuxième commande n’est pas affectée par le fait que son entrée ne provient pas d’un clavier.

Bien que toutes les commandes Linux disposent de ces trois flux standard, elles n’acceptent pas toutes la sortie stdout d’une autre commande comme entrée dans leur stdin. En d’autres termes, certaines commandes ne peuvent pas recevoir d’entrée via un pipe.

La commande `xargs` permet de créer des pipelines d’exécution en utilisant les flux de données standard. Grâce à `xargs`, nous pouvons permettre à des commandes comme `echo`, `rm` et `mkdir` d’accepter l’entrée standard comme arguments.

Fonctionnement de la commande `xargs`

`xargs` accepte les entrées via un pipe ou depuis un fichier. Elle utilise cette entrée comme paramètres pour les commandes spécifiées. Si aucune commande n’est précisée, `xargs` utilise par défaut `echo`.

On peut ainsi observer comment `xargs` génère toujours une seule ligne de sortie, même à partir d’une entrée multiligne.

Si l’on utilise l’option `-1` (lister un fichier par ligne) avec `ls`, on obtient une colonne unique de noms de fichiers.

ls -1 ./*.sh

Ceci liste les fichiers de script shell présents dans le répertoire actuel.

Comme prévu, nous obtenons une seule colonne. Que se passe-t-il si nous passons cette sortie à travers `xargs` ?

ls -1 ./*.sh | xargs

La sortie est écrite dans la fenêtre du terminal, comme un long flux de texte.

C’est cette capacité qui permet à `xargs` d’ajouter des paramètres à d’autres commandes.

Utilisation de `xargs` avec `wc`

Nous pouvons utiliser `xargs` pour que `wc` compte aisément les mots, caractères et lignes dans plusieurs fichiers.

ls *.page | xargs wc

Voici le déroulement des opérations :

La commande `ls` liste les fichiers `*.page` et transmet cette liste à `xargs`.

`xargs` transmet les noms de fichiers à `wc`.

`wc` traite les noms de fichiers comme s’ils avaient été reçus en tant que paramètres de ligne de commande.

Les statistiques de chaque fichier sont affichées avec un total global.

Utilisation interactive avec `xargs`

L’option `-p` (interactive) permet à `xargs` de demander une confirmation avant de poursuivre l’exécution.

Si nous passons une série de noms de fichiers à `touch` via `xargs`, la commande `touch` va créer ces fichiers.

echo 'one two three' | xargs -p touch

La commande qui va être exécutée est affichée et `xargs` attend une réponse avec `y` ou `Y` pour confirmer, ou `n` ou `N` pour annuler, suivi d’un appui sur la touche Entrée.

Si l’on appuie simplement sur la touche Entrée, la réponse sera interprétée comme « n ». La commande ne sera exécutée que si l’on tape « y » ou « Y ».

Nous avons tapé « y » puis appuyé sur Entrée. On peut vérifier que les fichiers ont bien été créés à l’aide de la commande `ls`.

ls one two three

Utilisation de `xargs` avec plusieurs commandes

Il est possible d’utiliser plusieurs commandes avec `xargs` grâce à l’option `-I` (arguments initiaux).

Cette option définit une « chaîne de remplacement ». Chaque fois que le jeton de cette chaîne apparaît dans la ligne de commande, les valeurs fournies à `xargs` sont insérées.

Utilisons la commande `tree` pour observer les sous-répertoires du répertoire actuel. L’option `-d` (répertoire) fait en sorte que `tree` ignore les fichiers et ne rapporte que les répertoires.

tree -d

Nous constatons la présence d’un unique sous-répertoire nommé « images ».

Dans un fichier nommé « repertoire.txt », nous avons listé des noms de répertoires que nous souhaitons créer. Nous pouvons consulter le contenu de ce fichier en utilisant la commande `cat`.

cat directories.txt

Nous allons utiliser le contenu de ce fichier comme données d’entrée pour `xargs`. Voici la commande que nous allons exécuter :

cat directories.txt | xargs -I % sh -c 'echo %; mkdir %'

Cette commande se décompose comme suit :

`cat directory.txt |`: Cette partie envoie le contenu du fichier `directrories.txt` (les noms des nouveaux répertoires) à `xargs`.
`xargs -I%`: Ceci définit une « chaîne de remplacement » avec le jeton « % ».
`sh -c`: Ceci démarre un nouveau sous-shell. L’option `-c` (commande) indique au shell de lire les commandes à partir de la ligne de commande.
`’echo %; mkdir %’`: Chaque occurrence du jeton « % » sera remplacée par les noms de répertoires transmis par `xargs`. La commande `echo` affichera le nom du répertoire ; la commande `mkdir` créera le répertoire.

Les répertoires sont listés un par un.

On peut à nouveau utiliser `tree` pour vérifier que les répertoires ont été créés.

tree -d

Copie de fichiers vers plusieurs destinations

Il est possible d’utiliser `xargs` pour copier des fichiers vers plusieurs destinations avec une seule commande.

Nous allons passer les noms de deux répertoires à `xargs` en tant que paramètres d’entrée. Nous indiquerons à `xargs` de transmettre un seul de ces paramètres à la fois à la commande qu’elle exécute.

Dans ce cas, la commande est `cp`. L’effet sera donc d’appeler `cp` deux fois, à chaque fois avec l’un des deux répertoires comme paramètre de ligne de commande. Le paramètre `xargs` qui permet cela est l’option `-n` (nombre maximum). Nous allons fixer ce nombre à un.

Nous utilisons également l’option `-v` (verbeux) avec `cp` pour afficher ce qui se passe.

echo ~/Backups/ ~/Documents/page-files/ | xargs -n 1 cp -v ./*.page

Les fichiers sont copiés dans les deux répertoires, un répertoire à la fois. La commande `cp` affiche chaque action de copie de fichier, ce qui nous permet de suivre le processus.

Suppression de fichiers dans des répertoires imbriqués

Si les noms de fichiers contiennent des espaces et des caractères spéciaux, comme des sauts de ligne, `xargs` ne pourra pas les interpréter correctement. Pour résoudre ce problème, on peut utiliser l’option `-0` (terminateur nul). Ceci indique à `xargs` d’utiliser le caractère nul comme délimiteur final pour les noms de fichiers.

Nous allons utiliser la commande `find` dans cet exemple. `find` dispose de sa propre option pour gérer les espaces et les caractères spéciaux dans les noms de fichiers. Il s’agit de l’option `-print0` (nom complet, caractère nul).

find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}"

Cette commande se décompose comme suit :

`find . -name « *.png » -type f`: la commande `find` va effectuer une recherche dans le répertoire actuel « . » pour les objets dont les noms correspondent à « *.png » et qui sont des fichiers (type `-f`).
`-print0`: les noms seront terminés par un caractère nul, et les espaces et caractères spéciaux seront pris en compte.
`xargs -0`: `xargs` considérera également que les noms de fichiers sont terminés par un caractère nul, et les espaces et caractères spéciaux ne poseront aucun problème.
`rm -v -rf « {} »`: La commande `rm` sera verbeuse et rapportera ce qui se passe (`-v`). Elle sera récursive (`-r`) et explorera les sous-répertoires imbriqués, et supprimera les fichiers sans demander de confirmation (`-f`). Le « {} » sera remplacé par chaque nom de fichier.

Tous les sous-répertoires sont explorés et les fichiers correspondant au critère de recherche sont supprimés.

Suppression de répertoires imbriqués

Supposons que nous souhaitions supprimer un ensemble de sous-répertoires imbriqués. La commande `tree` nous permet de les visualiser.

tree -d

find . -name "level_one" -type d printo | xargs -o rm -v -rf "{}"

Cette commande va utiliser la commande `find` pour effectuer une recherche récursive dans le répertoire actuel. La cible de la recherche est un répertoire nommé « level_one ». Les noms de répertoire sont transmis via `xargs` à `rm`.

Les seules différences notables entre cette commande et la précédente sont que le terme de recherche est le nom du répertoire supérieur et que l’option `-type d` indique à `find` de rechercher des répertoires et non des fichiers.

Le nom de chaque répertoire est affiché au fur et à mesure de sa suppression. Nous pouvons vérifier avec `tree` :

tree -d

Tous les sous-répertoires imbriqués ont été supprimés.

Suppression de tous les fichiers sauf un type spécifique

Nous pouvons utiliser `find`, `xargs` et `rm` pour supprimer tous les fichiers à l’exception de ceux d’un type particulier que nous souhaitons conserver. L’approche est un peu contre-intuitive : on indique le type de fichier que l’on souhaite conserver, et non celui des fichiers que l’on veut supprimer.

L’option `-not` indique à `find` de renvoyer les noms des fichiers qui ne correspondent pas au critère de recherche. Nous utilisons à nouveau l’option `-I` (arguments initiaux) avec `xargs`. Cette fois, le jeton de chaîne de remplacement que nous définissons est « {} » . Le comportement sera le même qu’avec le jeton « % » utilisé précédemment.

find . -type f -not - name "*.sh" -print0 | xargs -0 -I {} rm -v {}

Nous pouvons vérifier avec `ls`. Les seuls fichiers restants dans le répertoire sont ceux qui correspondent au critère de recherche « *.sh ».

ls -l

Création d’une archive avec `xargs`

Nous pouvons utiliser `find` pour rechercher des fichiers et les transmettre via `xargs` à `tar`, afin de créer un fichier archive.

Nous allons effectuer une recherche dans le répertoire actuel. Le critère de recherche est « *.page », ce qui permettra de trouver les fichiers « .page ».

find ./ - name "*.page" -type f -print0 | xargs -0 -tar -cvzf page_files.tar.gz

Les fichiers sont listés, comme prévu, pendant la création de l’archive.

Le rôle d’intermédiaire

Il est parfois nécessaire d’utiliser un outil intermédiaire pour effectuer des enchaînements complexes. `xargs` permet de connecter des commandes qui peuvent produire des informations avec des commandes qui ne sont pas conçues pour recevoir ces informations directement.

`xargs` et `find` disposent d’un grand nombre d’options. Nous vous encourageons à consulter leurs pages de manuel pour en savoir plus.