Wie verwende ich> in einem xargs-Befehl?

160

Ich möchte einen Bash-Befehl finden, mit dem ich jede Datei in einem Verzeichnis durchsuchen und die Ausgabe dieses Greps in eine separate Datei schreiben kann. Meine Vermutung wäre gewesen, so etwas zu tun

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

aber soweit ich weiß, mag xargs die doppelten Anführungszeichen nicht. Wenn ich jedoch die doppelten Anführungszeichen entferne, leitet der Befehl die Ausgabe des gesamten Befehls in eine einzelne Datei mit dem Namen '{}'. Out anstatt in eine Reihe einzelner Dateien um.

Kennt jemand eine Möglichkeit, dies mit xargs zu tun? Ich habe dieses grep-Szenario nur als Beispiel verwendet, um mein Problem mit xargs zu veranschaulichen, sodass Lösungen, die xargs nicht verwenden, für mich nicht so zutreffend sind.

Jesse Shieh
quelle

Antworten:

201

Machen Sie nicht den Fehler, dies zu tun:

sh -c "grep ABC {} > {}.out"

Dies wird unter vielen Bedingungen, einschließlich funky Dateinamen, brechen und ist unmöglich, richtig zu zitieren. Sie {}müssen immer ein einzelnes, vollständig separates Argument für den Befehl sein, um Fehler bei der Code-Injektion zu vermeiden. Was Sie tun müssen, ist Folgendes:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Gilt xargsauch für find.

Verwenden Sie xargs übrigens niemals ohne diese -0Option (es sei denn, Sie verwenden nur sehr selten und kontrolliert einmalig interaktiv, wenn Sie sich keine Sorgen um die Zerstörung Ihrer Daten machen).

Auch nicht analysieren ls. Je. Verwenden Sie Globbing oder findstattdessen:http://mywiki.wooledge.org/ParsingLs

Verwenden Sie findfür alles, was eine Rekursion benötigt, und eine einfache Schleife mit einem Glob für alles andere:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

oder nicht rekursiv:

for file in *; do grep "$file" > "$file.out"; done

Beachten Sie die ordnungsgemäße Verwendung von Anführungszeichen.

lhunath
quelle
Upvoted aber ein Zweifel regd. nicht xargsohne verwenden -0: Dies gilt nur, wenn Sie finddie Ausgabe mit xargs, oder? Wenn ich es tue, xargs -a <input_file>wie würde ich das verwenden? Die meisten Befehle mögen grepAusgaben mit \nund nicht. \0.Die einzige Möglichkeit, dies trzu umgehen, besteht darin, sie erneut zu verwenden, um dies möglicherweise zu beheben. Aber warum ist es wichtig, es nur mit zu verwenden -0?
Legends2k
3
@ legends2k, denn wenn Sie nicht verwenden -0, xargsnehmen Sie Ihre Dateinamen und brechen alle Leerzeichen, Anführungszeichen und Backslashes in ihnen. Sie sollten xargsals Werkzeug einfach vergessen . Wenn Sie Zeilen haben, verwenden Sie eine Bash-Schleife, um die Zeilen zu iterieren: while read line; do <command> "$REPLY"; done < file-with-linesodercommand | while ...
lhunath
1
Wow, wusste nichts davon, danke für das Detail! Aus Gründen der Portabilität (da nicht alle xargsGNUs sind) xargsmuss dies vermieden werden, es sei denn, man kann es mit verwenden -0. Danke dir.
Legends2k
1
Obwohl ich die ausführliche Erklärung für diesen speziellen Anwendungsfall zu schätzen weiß, geht es bei der Frage um die Umleitung der Ausgabe von xargs, bei der es nicht immer um das Parsen lsoder Verwenden geht sh -c. Dies beantwortet die Frage nicht im geringsten, sondern ist das erste Google-Ergebnis für die Frage, das nur zur Verwirrung beiträgt.
Pandasauce
1
@Ihunath, Hallo, deine Antwort funktioniert gut für mich. Aber könnten Sie eine detaillierte Erklärung oder Links dazu geben xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? Insbesondere die Regeln für eingebettete (doppelte) Anführungszeichen und das "-" Symbol am Ende. Vielen Dank
Scott Yang
40

Eine Lösung ohne xargsist die folgende:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... und das gleiche kann gemacht werden xargs , es stellt sich heraus:

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Bearbeiten : einfache Anführungszeichen nach Bemerkung von lhunath hinzugefügt .

Stephan202
quelle
Er sagte, er möchte Xargs verwenden. Ich habe auch eine Lösung ohne sie gepostet, aber gelöscht, als ich sah, dass er xargs brauchte.
Zifre
Du hast recht. Der Grund, warum ich meine Antwort gepostet habe, war, dass es besser ist, eine alternative Lösung zu haben, um die Arbeit zu erledigen, als überhaupt keine. Es stellt sich heraus, dass ich auf dem richtigen Weg bin, um die gewünschte Antwort zu finden (dh den sh-c-Trick).
Stephan202
14

Ich gehe davon aus, dass Ihr Beispiel nur ein Beispiel ist und dass Sie es möglicherweise für andere Dinge benötigen. GNU Parallel http://www.gnu.org/software/parallel/ kann Ihre Rettung sein. Es ist kein zusätzliches Anführungszeichen erforderlich, solange Ihre Dateinamen nicht \ n enthalten:

ls | parallel "grep ABC {} > {}.out"

Wenn Sie Dateinamen mit \ n haben:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

Als zusätzlichen Bonus erhalten Sie die Jobs parallel ausgeführt.

Sehen Sie sich die Intro-Videos an, um mehr zu erfahren: http://pi.dk/1

Bei der 10-Sekunden-Installation wird versucht, eine vollständige Installation durchzuführen. Wenn dies fehlschlägt, eine persönliche Installation. Wenn dies fehlschlägt, eine minimale Installation:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Wenn Sie es auf einen Server verschieben müssen, auf dem GNU Parallel nicht installiert ist, versuchen Sie es parallel --embed.

Ole Tange
quelle