utiliser xargs pour grep plusieurs modèles

12

J'ai un fichier qui contient des termes que je souhaite utiliser, chaque terme représentant une ligne dans le fichier. Je pensais que je pouvais faire ça avec xargs. Ce que je peux glaner à partir d'exemples de la page de manuel comme celle-ci

find ./work -print0 | xargs -0 rm

est que xargs ajoute la sortie de la commande pre-pipe à la fin de ses arguments. Donc, si la recherche est retournée report.doc, alors xargs serait construit rm report.doc. Cette compréhension est-elle correcte?

Donc, comme je veux que les valeurs de mon fichier soient au milieu de la commande grep, je dois spécifier un espace réservé. En jouant, j'ai essayé {}, mais cela n'a pas fonctionné:

$> cat strings.txt | xargs grep {} subdirectory/*
grep: string1: No such file or directory
grep: string2: No such file or directory

Xargs est-il le bon outil? Si oui, quelle est la syntaxe?

user394
la source

Réponses:

17

Oui, find ./work -print0 | xargs -0 rmexécutera quelque chose comme rm ./work/a "work/b c" .... Vous pouvez vérifier avec echo, find ./work -print0 | xargs -0 echo rmaffichera la commande qui sera exécutée (sauf que l'espace blanc sera échappé de manière appropriée, bien que echocela ne le montre pas).

Pour arriver xargsà mettre les noms au milieu, vous devez ajouter -I[string], où [string]est ce que vous voulez remplacer par l'argument, dans ce cas, vous utiliseriez -I{}, par exemple <strings.txt xargs -I{} grep {} directory/*.

Ce que vous voulez réellement utiliser, c'est grep -F -f strings.txt:

-F, --fixed-strings
  Interpret PATTERN as a  list  of  fixed  strings,  separated  by
  newlines,  any  of  which is to be matched.  (-F is specified by
  POSIX.)
-f FILE, --file=FILE
  Obtain  patterns  from  FILE,  one  per  line.   The  empty file
  contains zero patterns, and therefore matches nothing.   (-f  is
  specified by POSIX.)

Ainsi grep -Ff strings.txt subdirectory/*, toutes les occurrences de n'importe quelle chaîne seront trouvées en strings.txttant que littéral, si vous supprimez l' -Foption, vous pouvez utiliser des expressions régulières dans le fichier. Vous pouvez grep -F "$(<strings.txt)" directory/*également l' utiliser . Si vous voulez vous entraîner find, vous pouvez utiliser les deux derniers exemples du résumé. Si vous souhaitez effectuer une recherche récursive au lieu du premier niveau uniquement, vous disposez de quelques options, également dans le résumé.

Sommaire:

# grep for each string individually.
<strings.txt xargs -I{} grep {} directory/*

# grep once for everything
grep -Ff strings.txt subdirectory/*
grep -F "$(<strings.txt)" directory/*

# Same, using file
find subdirectory -maxdepth 1 -type f -exec grep -Ff strings.txt {} +
find subdirectory -maxdepth 1 -type f -print0 | xargs -0 grep -Ff strings.txt

# Recursively
grep -rFf strings.txt subdirectory
find subdirectory -type f -exec grep -Ff strings.txt {} +
find subdirectory -type f -print0 | xargs -0 grep -Ff strings.txt

Vous pouvez utiliser l' -loption pour obtenir uniquement le nom de chaque fichier correspondant si vous n'avez pas besoin de voir la ligne réelle:

-l, --files-with-matches
  Suppress  normal  output;  instead  print the name of each input
  file from which output would normally have  been  printed.   The
  scanning  will  stop  on  the  first match.  (-l is specified by
  POSIX.)
Kevin
la source
4

xargspeut ne pas être le meilleur outil que je suggérerais fgrepsi votre fichier de chaînes à faire correspondre ne contient que des chaînes, pas des expressions régulières.

fgrep -f strings.txt subdirectory/*

Je suggère fgrepcomme Unix traditionnel grepet egrepn'avait pas l'option "-f". Je crois que GNU grepet egrepont l'option "-f", donc si votre fichier contient des expressions régulières, vous voudrez utiliser une version GNU.

Bruce Ediger
la source