Pourquoi les xargs sont-ils nécessaires?

25

Supposons que je veuille supprimer tous les fichiers d'un répertoire à l'exception d'un nommé "notes.txt". Je ferais cela avec le pipeline, ls | grep -v "notes.txt" | xargs rm. Pourquoi ai-je besoin de xargs si la sortie du second canal est l'entrée que rm doit utiliser?

Par souci de comparaison, le pipeline echo "#include <knowledge.h>" | cat > foo.cinsère le texte en écho dans le fichier sans utiliser de xargs. Quelle est la différence entre ces deux pipelines?

seewalker
la source
3
Vous ne devez pas utiliser ls | grep -v "notes.txt" | xargs rmpour supprimer tout sauf pour notes.txt, ou en général, ne jamais analyser la lssortie . Votre commande serait interrompue si un seul fichier contenait un espace, par exemple. La manière la plus sûre serait rm !(notes.txt)dans Bash (avec shopt -s extglobset), ou rm ^notes.txtdans Zsh (avec EXTENDED_GLOB) etc.
slhck
Pour éviter les espaces, vous pourriez faire find . -maxdepth 1 -mindepth 1 -print0 | xargs -0au lieu de ls | xargs:-)
flob

Réponses:

35

Vous confondez deux types d'entrée très différents: STDIN et arguments. Les arguments sont une liste de chaînes fournies à la commande au démarrage, généralement en les spécifiant après le nom de la commande (par exemple, echo these are some argumentsou rm file1 file2). STDIN, d'autre part, est un flux d'octets (parfois du texte, parfois non) que la commande peut (éventuellement) lire après son démarrage. Voici quelques exemples (notez que cela catpeut prendre des arguments ou STDIN, mais cela fait des choses différentes avec eux):

echo file1 file2 | cat    # Prints "file1 file2", since that's the stream of
                          # bytes that echo passed to cat's STDIN
cat file1 file2    # Prints the CONTENTS of file1 and file2
echo file1 file2 | rm    # Prints an error message, since rm expects arguments
                         # and doesn't read from STDIN

xargs peut être considéré comme la conversion d'une entrée de style STDIN en arguments:

echo file1 file2 | cat    # Prints "file1 file2"
echo file1 file2 | xargs cat    # Prints the CONTENTS of file1 and file2

echo fait en fait plus ou moins l'inverse: il convertit ses arguments en STDOUT (qui peut être canalisé vers le STDIN d'une autre commande):

echo file1 file2 | echo    # Prints a blank line, since echo doesn't read from STDIN
echo file1 file2 | xargs echo    # Prints "file1 file2" -- the first echo turns
                                 # them from arguments into STDOUT, xargs turns
                                 # them back into arguments, and the second echo
                                 # turns them back into STDOUT
echo file1 file2 | xargs echo | xargs echo | xargs echo | xargs echo    # Similar,
                                 # except that it converts back and forth between
                                 # args and STDOUT several times before finally
                                 # printing "file1 file2" to STDOUT.
Gordon Davisson
la source
9

catprend des entrées de STDINet rmne le fait pas. Pour ces commandes, vous devez xargsparcourir STDINligne par ligne et exécuter les commandes avec les paramètres de ligne de commande.

Alex P.
la source