web-dev-qa-db-fra.com

Que fait "xargs grep"?

Je connais la commande grep et je découvre les fonctionnalités de xargs. J'ai donc lu la page this qui donne des exemples d'utilisation de la commande xargs.

Le dernier exemple, exemple 10, me confond. Il est écrit "La commande xargs exécute la commande grep pour rechercher tous les fichiers (parmi les fichiers fournis par la commande find) contenant une chaîne" stdlib.h ""

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Cependant, quelle est la différence en utilisant simplement

$ find . -name '*.c' | grep 'stdlib.h'

?

De toute évidence, je suis toujours aux prises avec ce que fait exactement xargs, alors toute aide est la bienvenue!

24
AlphaOmega
$ find . -name '*.c' | grep 'stdlib.h'

Cela conduit la sortie (stdout) * de find à (stdin of) * grep 'stdlib.h' sous forme de texte (c’est-à-dire que les noms de fichiers sont traités comme du texte). grep fait son travail habituel et trouve les lignes correspondantes dans ce texte (tous les noms de fichiers contenant eux-mêmes le motif). Le contenu des fichiers n'est jamais lu.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Ceci construit un commandegrep 'stdlib.h' auquel chaque résultat de find est un argument - donc cela cherchera des correspondances à l'intérieur chaque fichier trouvé par find (xargs peut être considéré comme étant en train de tourner stdin en arguments des commandes données) *

Utilisez -type f dans votre commande find ou vous obtiendrez des erreurs de grep pour les répertoires correspondants. De plus, si les noms de fichiers comportent des espaces, xargs se trompera mal, utilisez donc le séparateur null en ajoutant -print0 et xargs -0 pour des résultats plus fiables:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* ajout de ces points explicatifs supplémentaires comme suggéré dans le commentaire de @cat

29
Zanna

xargs prend son entrée standard et le transforme en arguments de ligne de commande.

find . -name '*.c' | xargs grep 'stdlib.h' est très similaire à

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

Et donnera les mêmes résultats tant que la liste des noms de fichiers n'est pas trop longue pour une seule ligne de commande. (Linux prend en charge les mégaoctets de texte sur une seule ligne de commande, vous n'avez donc généralement pas besoin de xargs.)


Mais ces deux sont nuls, parce que ils cassent si vos noms de fichiers contiennent des espaces . find -print0 | xargs -0 fonctionne à la place, mais il en va de même

find . -name '*.c' -exec grep 'stdlib.h' {} +

Cela n'indique jamais les noms de fichiers: find les regroupe dans une grande ligne de commande et exécute grep directement.

\; au lieu de + exécute grep séparément pour chaque fichier, ce qui est beaucoup plus lent. Ne fais pas ça. + étant une extension GNU, vous avez donc besoin de xargs pour le faire efficacement si vous ne pouvez pas supposer que GNU find.


Si vous omettez xargs, find | grep fait correspondre ses motifs à la liste des noms de fichiers que find imprime.

Donc, à ce moment-là, vous pourriez aussi bien faire find -name stdlib.h. Bien sûr, avec -name '*.c' -name stdlib.h, vous n'obtiendrez aucune sortie, car ces modèles ne peuvent pas être identiques, et le comportement par défaut de find consiste à utiliser ET les règles ensemble.

Substituez less à tout moment du processus pour voir la sortie produite par une partie du pipeline.


Pour en savoir plus: http://mywiki.wooledge.org/BashFAQ contient des informations intéressantes.

6
Peter Cordes

En règle générale, xargsest utilisé dans les cas où vous dirigeriez (avec le symbole |) quelque chose d'une commande à l'autre (Command1 | Command2), mais la sortie de la première commande n'est pas correctement reçue en tant qu'entrée pour la seconde commande.

Cela se produit généralement lorsque la deuxième commande ne traite pas correctement les données saisies via Standard In (stdin) (par exemple: Plusieurs lignes en entrée, la configuration des lignes, les caractères utilisés en tant que saisie, plusieurs paramètres en entrée, le type de données reçu en tant que entrée, etc.). Pour vous donner un exemple rapide, testez les éléments suivants:

Exemple 1:

ls | echo - Cela ne fera rien car echone sait pas comment gérer l'entrée qu'il reçoit. Maintenant, dans ce cas, si nous utilisons xargsname__, l’entrée sera traitée de manière à pouvoir être gérée correctement par echo(par exemple: comme une seule ligne d’information).

ls | xargs echo - Ceci affichera toutes les informations de lssur une seule ligne

Exemple 2:

Disons que j'ai plusieurs fichiers goLang dans un dossier appelé go. Je les chercherais avec quelque chose comme ça:

find go -name *.go -type f | echo - Mais si le symbole de canal est présent et que echose trouve à la fin, cela ne fonctionnerait pas.

find go -name *.go -type f | xargs echo - Ici, cela fonctionnerait grâce à xargsmais si je voulais chaque réponse de la commande findsur une seule ligne, je procéderais comme suit:

find go -name *.go -type f | xargs -0 echo - Dans ce cas, le même résultat de findserait affiché par echoname__.

Des commandes telles que cp, echo, rm, less et d'autres nécessitant un meilleur moyen de gérer les entrées ont un avantage lorsqu'elles sont utilisées avec xargsname__.

5
Luis Alvarado

xargs est utilisé pour générer automatiquement des arguments de ligne de commande basés (généralement) sur une liste de fichiers.

Donc, en considérant quelques alternatives à l’utilisation de la commande suivante xargs:

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

Il y a plusieurs raisons pour l'utiliser à la place d'autres options qui n'étaient pas mentionnées à l'origine dans d'autres réponses:

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\; générera un processus grep pour chaque fichier. C'est généralement considéré comme une mauvaise pratique et peut entraîner une lourde charge pour le système si de nombreux fichiers sont trouvés.
  2. S'il y a beaucoup de fichiers, une commande grep 'stdlib.h' $(find . -name '*.c') échouera probablement car la sortie de l'opération $(...) dépassera la longueur maximale de la ligne de commande du shell.

Comme indiqué dans d'autres réponses, la raison d'utiliser l'argument -print0 pour find dans ce scénario et l'argument -0 pour xargs est que les noms de fichiers comportant certains caractères (par exemple, des guillemets, des espaces ou même des nouvelles lignes) sont toujours gérés correctement.

4
Michael Firth