Verwenden von Xargs, um mehrere Muster zu erfassen

12

Ich habe eine Datei mit Begriffen, nach denen ich suchen möchte, wobei jeder Begriff eine Zeile in der Datei enthält. Ich dachte, ich könnte das mit xargs machen. Was ich aus Beispielen dieser Manpage entnehmen kann

find ./work -print0 | xargs -0 rm

ist, dass xargs die Ausgabe des Pre-Pipe-Befehls an das Ende seiner Argumente anfügt. Wenn also der Fund zurückgegeben würde report.doc, würde xargs konstruieren rm report.doc. Ist dieses Verständnis richtig?

Da sich die Werte in meiner Datei also in der Mitte des Befehls grep befinden sollen, muss ein Platzhalter angegeben werden. Beim Herumspielen habe ich es versucht {}, aber es hat nicht funktioniert:

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

Ist xargs das richtige Werkzeug? Wenn ja, wie lautet die Syntax?

user394
quelle

Antworten:

17

Ja, find ./work -print0 | xargs -0 rmwird so etwas ausführen rm ./work/a "work/b c" .... Sie können mit überprüfen echo, find ./work -print0 | xargs -0 echo rmwird den Befehl drucken, der ausgeführt wird (außer Leerzeichen werden entsprechend maskiert, obwohl das echonicht zeigt).

Um xargsdie Namen in die Mitte zu setzen, müssen Sie hinzufügen -I[string], wo [string]Sie durch das Argument ersetzt werden möchten, in diesem Fall würden Sie -I{}z <strings.txt xargs -I{} grep {} directory/*.

Was Sie tatsächlich verwenden möchten, ist 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.)

So grep -Ff strings.txt subdirectory/*werden alle Vorkommen einer Zeichenfolge in finden strings.txtals wörtliche, wenn Sie die Drop - -FOption können Sie reguläre Ausdrücke in der Datei verwenden. Sie könnten auch tatsächlich verwenden grep -F "$(<strings.txt)" directory/*. Wenn Sie üben möchten find, können Sie die letzten beiden Beispiele in der Zusammenfassung verwenden. Wenn Sie anstelle der ersten Ebene eine rekursive Suche durchführen möchten, haben Sie auch in der Zusammenfassung einige Optionen.

Zusammenfassung:

# 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

Sie können die -lOption verwenden, um nur den Namen jeder übereinstimmenden Datei abzurufen, wenn Sie die aktuelle Zeile nicht sehen müssen:

-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
quelle
4

xargsist möglicherweise nicht das beste Tool, das ich vorschlagen würde, fgrepwenn Ihre zu vergleichende Datei nur Zeichenfolgen und keine regulären Ausdrücke enthält.

fgrep -f strings.txt subdirectory/*

Ich schlage fgrepals traditionelles Unix vor grepund egrephatte nicht die Option "-f". Ich glaube, dass GNU grepund egrepdie Option "-f" haben. Wenn Ihre Datei also reguläre Ausdrücke enthält, möchten Sie eine GNU-Version verwenden.

Bruce Ediger
quelle