Wann braucht man xargs?

134

Der xargsBefehl verwirrt mich immer. Gibt es eine allgemeine Regel dafür?

Betrachten Sie die beiden folgenden Beispiele:

$ \ls | grep Cases | less

druckt die Dateien, die mit 'Cases' übereinstimmen, aber das Ändern des Befehls toucherfordert xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch
Zaid
quelle

Antworten:

143

Der Unterschied besteht darin, welche Daten das Zielprogramm akzeptiert.

Wenn Sie nur eine Pipe verwenden, werden Daten in STDIN (dem Standardeingabestream) als unformatierter Datenstapel empfangen, der zeilenweise sortiert werden kann. Einige Programme akzeptieren ihre Befehle jedoch nicht in der Standardeinstellung, sondern erwarten, dass sie in den Argumenten des Befehls angegeben werden. Zum Beispiel touchnimmt einen Dateinamen als Parameter in der Befehlszeile wie folgt: touch file1.txt.

Wenn Sie ein Programm haben , die Dateinamen ausgibt , auf Standard aus und sie verwenden möchten als Argumente zu touch, müssen Sie verwenden , xargswelche die STDIN - Stream - Daten liest und konvertiert jede Zeile in den Raum getrennt Argumente für den Befehl.

Diese beiden Dinge sind äquivalent:

# touch file1.txt
# echo file1.txt | xargs touch

Verwenden xargsSie es nur, wenn Sie genau wissen, was es tut und warum es benötigt wird. Oft gibt es einen besseren Weg, als xargsdie Konvertierung zu erzwingen. Der Konvertierungsprozess ist auch mit potenziellen Fallstricken wie Flucht und Worterweiterung usw. behaftet.

Caleb
quelle
2
Die Warnung fühlt sich für mich ein wenig an. Von den beiden gebräuchlichen Optionen zum Abrufen eines Streams auf eine Befehlszeile ( xargsund $(...)) ist xargs weitaus sicherer als das Ersetzen von Befehlen. Und ich kann mich nicht erinnern, jemals auf einen legitimen Dateinamen mit einer neuen Zeile gestoßen zu sein. Sind die Probleme mit der Ersetzung von Befehlen und der Erweiterung von Wörtern nicht gleichbedeutend mit Problemen mit der Ersetzung von Befehlen und nicht mit Xargs?
19.
6
@camh: Sie sind potenzielle Fallstricke bei beiden. In der Shell müssen Sie sich Sorgen machen, dass Dateinamen in Leerzeichen, Tabulatoren und Zeilenumbrüche aufgeteilt werden. In xargs müssen Sie sich nur um Zeilenumbrüche kümmern. In xargs können Sie, wenn Ihre Ausgabe korrekt formatiert ist, stattdessen Wörter / Dateinamen auf das NUL-Zeichen ( xargs -0) aufteilen , was in Verbindung mit nützlich ist find -print0.
Ken Bloom
Ruft xargsdas Programm über die Shell mit durch Leerzeichen getrennten Argumenten auf oder erstellt es die Argumentliste tatsächlich intern (z. B. zur Verwendung mit execv/ execp)?
Detly
1
Es erstellt es intern und verwendet execvp, also ist es sicher. Mit GNU xargs (wie es unter Linux und einigen anderen Betriebssystemen verwendet wird) können Sie Newline als Trennzeichen angeben -d \n, obwohl BSD xargs (OSX et al.) Diese Option anscheinend nicht unterstützt.
flauschiger
72

Um die bereits gegebenen Antworten zu erweitern, xargskönnen Sie eine coole Sache tun, die in der heutigen Multicore- und verteilten Computerlandschaft immer wichtiger wird: Sie können Jobs parallel verarbeiten.

Zum Beispiel:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

codiert * .wav => * .flac mit drei Prozessen gleichzeitig ( -P 3).

Amphetamachine
quelle
Beeindruckend. Ich hätte das vor einer Woche wissen müssen, als ich genau dasselbe tat (außer mit OGG) mit 50 GB WAVs. :)
Alois Mahdal
Warum nicht den Parameter -exec verwenden, den find hat?
Evgeny
3
@Evgeny Der -execParameter verarbeitet Jobs nicht parallel.
Amphetamachine
Gut zu wissen, dass das -0Argument toxargs bewirkt, dass das NULLZeichen als Begrenzer für das Eingabeelement betrachtet wird. find -print0NULL-getrennte Elemente ausgeben. Dies ist eine gute Vorgehensweise für Dateinamen, die Leerzeichen, Anführungszeichen oder andere Sonderzeichen enthalten können.
Dan Dascalescu
24

xargs ist besonders nützlich, wenn Sie eine Liste von Dateipfaden auf stdin haben und damit etwas anfangen möchten. Zum Beispiel:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Lassen Sie uns dies Schritt für Schritt untersuchen:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Mit anderen Worten, unsere Eingabe ist eine Liste von Pfaden, mit denen wir etwas tun möchten.

Um herauszufinden, was xargs mit diesen Pfaden macht, ist es ein netter Trick, echovor Ihrem Befehl Folgendes hinzuzufügen :

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

Das -n 1Argument lässt xargs jede Zeile in einen eigenen Befehl umwandeln. Der sed -i "s/color/colour/g"Befehl ersetzt alle Vorkommen von colormit colourfür die angegebene Datei.

Beachten Sie, dass dies nur funktioniert, wenn Ihre Pfade keine Leerzeichen enthalten. In diesem Fall sollten Sie nullterminierte Pfade als Eingabe für xargs verwenden, indem Sie das -0Flag übergeben. Eine Beispielverwendung wäre:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Das funktioniert genauso wie oben beschrieben, funktioniert aber auch, wenn einer der Pfade ein Leerzeichen enthält.

Dies funktioniert mit jedem Befehl, der Dateinamen als Ausgabe erzeugt, wie z. B. findoder locate. Wenn Sie es jedoch in einem Git-Repository mit vielen Dateien verwenden, ist es möglicherweise effizienter, es git grep -lstatt git ls-fileswie folgt zu verwenden:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Der git grep -l "color" "*.tex"Befehl gibt eine Liste von "* .tex" -Dateien aus, die den Ausdruck "Farbe" enthalten.

Sverre Rabbelier
quelle
1
Stimmt, aber wenn Sie dies gelernt haben, sollten Sie auch lernen, warum das Schleifen über Finds Ausgabe eine schlechte Praxis ist.
Wildcard
6

Ihr erstes Argument verdeutlicht den Unterschied recht gut.

\ls | grep Cases | lessErmöglicht das Durchsuchen der Liste der von lsund erstellten Dateinamen grep. Es spielt keine Rolle, dass es sich zufällig um Dateinamen handelt, sondern nur um Text.

\ls | grep Cases | xargs lessErmöglicht das Durchsuchen der Dateien, deren Namen vom ersten Teil des Befehls erzeugt werden. xargsVerwendet eine Liste von Dateinamen als Eingabe und einen Befehl in der Befehlszeile und führt den Befehl mit den Dateinamen in der Befehlszeile aus.

Bei der Verwendung unter Berücksichtigung xargs, bedenken Sie, dass es Eingang erwartet auf eine seltsame Weise formatiert: Leerzeichen getrennte, mit \, 'und "verwendet für die Quotierung (auf ungewöhnliche Art und Weise, weil \keine besondere in Anführungszeichen ist). Verwenden xargsSie nur, wenn Ihre Dateinamen keine Leerzeichen oder \'".

Gilles
quelle
@Gilles: xargs hat die -0, --nullMöglichkeit, das Leerzeichenproblem zu umgehen (das habe ich höchstwahrscheinlich von Ihnen erfahren :). xargIch gehe also davon aus, dass Sie sich auf einen No-Options- Aufruf beziehen, bin aber verwirrt, wenn Sie auf die Anführungszeichen verweisen. Haben Sie einen Link oder ein Beispiel dazu? .. (ps. | xargs lessist ein praktischer "Trick" +1 .. danke ..
Peter.O
4

In Ihrem Beispiel müssen Sie überhaupt nicht verwenden, xargsda findgenau und sicher das getan wird, was Sie tun möchten.

Genau das, was Sie verwenden möchten, findist:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

In diesem Beispiel -maxdepth 1bedeutet dies, dass nur im aktuellen Verzeichnis gesucht wird und keine Unterverzeichnisse erstellt werden. Standardmäßig sucht find in allen Unterverzeichnissen (was häufig gewünscht wird), sofern Sie es nicht mit maxdepth einschränken. Das {}ist der Name der Datei, die an ihrer Stelle ersetzt wird, und das +ist eine von zwei Befehlsende-Markierungen, die andere ;. Der Unterschied zwischen ihnen besteht darin, dass ;der Befehl für jede Datei +einzeln ausgeführt wird , während der Befehl für alle Dateien gleichzeitig ausgeführt wird. Beachten Sie jedoch, dass Ihre Shell wahrscheinlich versucht, sich ;selbst zu interpretieren. Sie müssen sie daher entweder mit \;oder schließen ';'. Ja, findhat eine Reihe von kleinen Ärgernissen wie diese, aber seine Kraft macht das mehr als wett.

Beides findund xargssind zunächst schwierig zu lernen. Damit Sie lernen , xargsversuchen , die Verwendung -poder --interactiveOption , die Ihnen den Befehl zeigt es im Begriff ist , auszuführen und fordern SieFormal auf, ob Sie es ausgeführt werden sollen.

In ähnlicher Weise findkönnen Sie -okanstelle von verwenden -exec, um Sie aufzufordern, ob Sie den Befehl ausführen möchten oder nicht.

Es kann jedoch vorkommen, dass Sie findnicht in der Lage sind, alles zu tun, was Sie möchten, und hier xargskommt es an. Der -execBefehl akzeptiert nur eine Instanz des {}Auftretens. Wenn Sie also einen Fehler erhalten find -type f -exec cp {} {}.bak \;, können Sie dies stattdessen so tun :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Weitere Informationen zu Run Commands finden Sie im GNU Findutils-Handbuch .

Ich erwähnte auch, dass dies findsicher das macht, was Sie wollen, da Sie beim Umgang mit Dateien auf Leerzeichen und andere Zeichen stoßen, die Probleme verursachen, xargswenn Sie nicht die Option -0oder --nullzusammen mit etwas verwenden, das stattdessen Eingabeelemente erzeugt, die mit einem Null-Zeichen abgeschlossen sind Leerzeichen.

aculich
quelle
@Wildcard-Dateinamen mit Leerzeichen oder Zeichen wie 'oder "können problematisch sein, wohingegen finddiese Fälle problemlos behandelt werden.
ACULICH
Ja, ich weiß. Siehe meine Antwort auf die verknüpfte Frage . Ich hätte diese Frage wahrscheinlich zu einer Aussage im obigen Kommentar umformulieren oder den Satz "Siehe die Frage ..." davor einfügen sollen. : D
Wildcard
1

xargs(zusammen mit find, sort, du, uniq, perlund ein paar andere) akzeptiert eine Befehlszeilenoption zu sagen : „STDIN eine Liste von Dateien hat, getrennt durch eine NUL (0x00) Byte“. Dies erleichtert den Umgang mit Dateinamen mit Leerzeichen und anderen lustigen Zeichen. Dateinamen enthalten keine NULs.

Waltinator
quelle
2
Ich denke du meinst "Dateinamen dürfen keine Nullen enthalten."
Amphetamachine