Xargs in die zweite Seite des Rohrs?

Xargs in die zweite Seite des Rohrs?

Ich versuche Folgendes zu tun:

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"

Ich erwarte, dass jede Zeile aus Datei1 der Wert für grep am Ende der dritten Pipe ist. Das funktioniert nicht wie erwartet.

Liegt das daran, dass -I{}die Suche nach zu ersetzenden Dingen eingestellt wird, sobald es das Rohr erreicht? Gibt es eine Möglichkeit, dies zu umgehen?

Antwort1

Das liegt daran, dass Sie eine Shell benötigen, um eine Pipe zu erstellen oder eine Umleitung durchzuführen. Beachten Sie, dass es sich catum einen Befehl zum Verketten handelt. Es macht wenig Sinn, ihn nur für eine Datei zu verwenden.

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}

TunnichtTun:

cat Datei1.txt | xargs -I{} sh -c 'cat Datei2.txt | grep -e {}'

da dies einer Schwachstelle bei der Befehlseinschleusung gleichkäme. Das {}würde im Codeargument erweitert werden und shdaher als Shellcode interpretiert werden. Wenn beispielsweise die Zeile von file1.txtwäre , $(reboot)würde das aufrufen reboot.

Das -e(oder Sie können auch verwenden --) ist ebenfalls wichtig. Ohne es hätten Sie Probleme mit regulären Ausdrücken, die mit beginnen -.

Sie können das oben genannte vereinfachen, indem Sie anstelle von Folgendem Umleitungen verwenden cat:

< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}

Oder übergeben Sie einfach die Dateinamen als Argument an, grepanstatt Umleitungen zu verwenden. In diesem Fall können Sie sogar das Folgende weglassen sh:

< file1.txt xargs -I{} grep -e {} file2.txt

Sie können auch angeben, grepdass in einem einzigen Aufruf nach allen regulären Ausdrücken gleichzeitig gesucht werden soll:

grep -f file1.txt file2.txt

Beachten Sie jedoch, dass es sich in diesem Fall nur um einen regulären Ausdruck für jede Zeile von handelt und file1.txtdie spezielle Anführungszeichenverarbeitung von nicht erfolgt xargs.

xargsbetrachtet seine Eingabe standardmäßig als eine Liste von leeren (bei einigen Implementierungen nur Leerzeichen und Tabulatoren, bei anderen alle in der [:blank:]Zeichenklasse des aktuellen Gebietsschemas) oder durch Zeilenumbrüche getrennten Wörtern, für die Backslashs und einfache und doppelte Anführungszeichen verwendet werden können, um die Trennzeichen zu maskieren (Zeilenumbrüche können jedoch nur durch Backslashs maskiert werden) oder sich gegenseitig.

Beispielsweise bei einer Eingabe wie:

 'a "b'\" "bar baz" x\
y

xargsohne -I{}würde passieren a "b", bar bazund x<newline>yzum Befehl.

Mit wird ein Wort pro Zeile abgerufen -I{}, xargses wird aber trotzdem eine zusätzliche Verarbeitung durchgeführt. Führende (aber nicht nachfolgende) Leerzeichen werden ignoriert. Leerzeichen werden nicht mehr als Trennzeichen betrachtet, aber die Anführungszeichenverarbeitung erfolgt trotzdem.

Bei der obigen Eingabe xargs -I{}würde ein a "b" foo bar x<newline>yArgument an den Befehl übergeben. Beachten Sie auch, dass dies in vielen Systemen, wie von POSIX gefordert, nicht funktioniert, wenn Wörter länger als 255 Zeichen sind. Alles in allem xargs -I{}ist das ziemlich nutzlos.

Wenn Sie möchten, dass jede Zeile wortwörtlich als Argument an den Befehl übergeben wird, können Sie die GNU- xargs -d '\n'Erweiterung verwenden:

< file1.txt xargs -d '\n' -n 1 grep file2.txt -e

(hier basierend auf einer anderen Erweiterung von GNU grep, die die Übergabe von Optionen nach Argumenten ermöglicht (vorausgesetzt, die Umgebung ist nicht POSIX-korrekt) oder portabel:

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh

Wenn Sie jeweilsWortin file1.txt(Anführungszeichen werden noch erkannt) im Gegensatz zu eachLiniegesucht werden soll (wodurch auch Ihr Problem mit den nachstehenden Leerzeichen umgangen werden kann, wenn Sie ohnehin ein Wort pro Zeile haben), können Sie xargs -n1allein anstelle von verwenden -I:

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh

Um führende und nachfolgende Leerzeichen zu entfernen (aber ohne die entsprechende Anführungszeichenverarbeitung xargs), können Sie auch Folgendes tun:

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt

Antwort2

Je nachdem, was Sie versuchen, ist es möglicherweise besser, den Vorgang xargsganz zu überspringen und stattdessen diese Lösung zu wählen:

grep -f file1.txt file2.txt

Dies unterscheidet sich von Ihrem ursprünglichen Befehl(sobald wir es wie in der Antwort von Stéphane Chazelas behoben haben) wie folgt:

  • Zeilen werden in der Reihenfolge gedruckt, in der sie vorkommen, file2.txtunabhängig davon, welchen Mustern sie entsprechen. In Ihrem Befehl werden alle Zeilen gedruckt, die dem ersten Muster entsprechen, dann alle Zeilen, die dem zweiten Muster entsprechen, und so weiter.
  • Zeilen, die mehr als einem Muster entsprechen, werden genau einmal gedruckt. In Ihrem Befehl werden sie einmal für jedes Muster gedruckt, dem sie entsprechen.
  • Mehrere Flags können einfacher verwendet werden, darunter sowohl als -vauch -c.

Die -fFlagge istspezifiziert durch POSIXund daher einigermaßen portierbar.

verwandte Informationen