cat a.txt | xargs -I % echo %
Im obigen Beispiel verwendet xargs echo %
das Befehlsargument. In einigen Fällen benötige ich jedoch mehrere Befehle, um das Argument anstelle eines zu verarbeiten. Zum Beispiel:
cat a.txt | xargs -I % {command1; command2; ... }
Aber xargs akzeptiert dieses Formular nicht. Eine Lösung, die ich kenne, ist, dass ich eine Funktion zum Umschließen der Befehle definieren kann, aber es ist keine Pipeline, ich bevorzuge es nicht. Gibt es eine andere Lösung?
while
Schleife leitet, die mehrere Befehle enthalten kann.Antworten:
... oder ohne nutzlosen Gebrauch von Katze :
Um einige der Feinheiten zu erklären:
Die Verwendung von
"$arg"
statt%
(und das Fehlen von-I
in derxargs
Befehlszeile) ist aus Sicherheitsgründen: Führen von Daten aufsh
der Argumente Liste Befehlszeile statt es in Code verhindert Inhalt ersetzt wird, dass Daten enthalten (wie zum Beispiel$(rm -rf ~)
eines besonders nehmen böswilliges Beispiel) von der Ausführung als Code.In ähnlicher Weise ist die Verwendung von
-d $'\n'
eine GNU-Erweiterung, die bewirktxargs
, dass jede Zeile der Eingabedatei als separates Datenelement behandelt wird. Entweder dies oder-0
(was NULs anstelle von Zeilenumbrüchen erwartet) ist erforderlich, um zu verhindern, dass xargs versucht, Shell-ähnliche (aber nicht ganz Shell-kompatible) Parsing auf den gelesenen Stream anzuwenden . (Wenn Sie keine GNU-xargs haben, können Sietr '\n' '\0' <a.txt | xargs -0 ...
ohne zeilenorientiertes Lesen verwenden-d
).Das
_
ist ein Platzhalter für$0
, sodass andere Datenwerte durchxargs
Werden$1
und Weiter hinzugefügt werden. Dies ist zufällig der Standardwertsatz, über den einefor
Schleife iteriert.quelle
sh -c
- nicht vertraut sind - beachten Sie, dass das Semikolon nach jedem Befehl nicht optional ist, selbst wenn es der letzte Befehl in der Liste ist.command1
undcommand2
; Ich habe später festgestellt, dass sie nicht notwendig sind.}
:sh -c '{ command1; command2; }' -- but it's not required at the end of a command sequence that doesn't use braces:
sh -c 'command1; command2'`%
Zeichen irgendwo in Ihre übergebene Zeichenfolge aufnehmensh -c
, ist dies anfällig für Sicherheitslücken: Ein Dateiname, der$(rm -rf ~)'$(rm -rf ~)'
(und das ist eine völlig legale Teilzeichenfolge, die in einem Dateinamen auf gängigen UNIX-Dateisystemen enthalten sein muss) enthält, führt zu einem sehr schlechten Tag .Mit GNU Parallel können Sie Folgendes tun:
Sehen Sie sich die Intro-Videos an, um mehr zu erfahren: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Aus Sicherheitsgründen wird empfohlen, zur Installation Ihren Paketmanager zu verwenden. Wenn Sie dies jedoch nicht tun können, können Sie diese 10-Sekunden-Installation verwenden.
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.
quelle
Dies ist nur ein weiterer Ansatz ohne Xargs oder Cat:
quelle
IFS
, werden führende und nachfolgende Leerzeichen in den Dateinamen ignoriert. Wenn Sie keine-r
Dateinamen mit wörtlichen Backslashes hinzufügen , werden diese Zeichen ignoriert.xargs
. (Dies ist schwer zu erweitern, um etwas Ähnliches wie die GNUxargs
--P<n>
Option zu tun )$ command | while read line; do c1 $line; c2 $line; done
Sie können verwenden
{} = Variable für jede Zeile in der Textdatei
quelle
file.txt
ein Datum mit$(rm -rf ~)
einem Teilstring enthalten?Eine Sache, die ich tue, ist, diese Funktion zu .bashrc / .profile hinzuzufügen:
dann kannst du Dinge wie tun
Das ist weniger ausführlich als xargs oder -exec. Sie können die Funktion auch so ändern, dass der Wert aus dem Lesevorgang an einer beliebigen Stelle in den Befehlen jeweils eingefügt wird, wenn Sie dieses Verhalten ebenfalls benötigen.
quelle
Ich bevorzuge einen Stil, der einen Trockenlaufmodus (ohne
| sh
) ermöglicht:Funktioniert auch mit Rohren:
quelle
-P
Option von GNU xargs verwenden möchten ... (Wenn nicht, verwende ich meistens-exec
onfind
, da meine Eingaben meistens Dateinamen sind)Ein bisschen spät zur Party.
Ich verwende das folgende Format, um meine Verzeichnisse vor der Migration mit Tausenden winziger Dateien zu komprimieren. Wenn Sie keine einfachen Anführungszeichen in Befehlen benötigen, sollte dies funktionieren.
Mit einigen Änderungen bin ich sicher, dass es für jemanden nützlich sein wird. Getestet in
Cygwin
(babun)find .
Hier finden-maxdepth 1
Gehen Sie nicht in! -path .
untergeordnete Verzeichnisse . / Aktueller Verzeichnispfad stimmt-type d
nur mit Verzeichnissen überein-print0
Ausgabe getrennt durch Null-Bytes \ 0| xargs
Pipe to xargs-0
Eingabe ist null getrennte Bytes-I @@
Platzhalter ist @@. Ersetzen Sie @@ durch Eingabe.bash -c '...'
Run Bash Befehl{...}
Befehl Gruppierung&&
nur nächsten Befehl ausführen , wenn die vorherige Befehl beendete erfolgreich (exit 0)Final
;
ist wichtig, sonst schlägt es fehl.Ausgabe:
2018 Juli Update:
Wenn Sie Hacks lieben und herumspielen, ist hier etwas Interessantes:
Ausgabe:
Erläuterung:
- Erstellen Sie eine einzelne Liner Skript und speichern sie in einer Variablen
-
xargs
liesta.txt
und führt es alsbash
Skript-
@@
stellt sicher , dass jedes Mal eine ganze Zeile übergeben wird- Putting
@@
nach--
stellt sicher ,@@
als Positionsparameter eingegeben genommenbash
Befehl, keinbash
StartOPTION
, das heißt wie sich-c
selbst, was bedeutetrun command
--
ist magisch, es funktioniert mit vielen anderen Dingen, dhssh
sogarkubectl
quelle
find . -type f -print0|xargs -r0 -n1 -P20 bash -c 'f="{}";ls -l "$f"; gzip -9 "$f"; ls -l "$f.gz"'
(Es ist etwas einfacher beim Konvertieren von Schleifen)"$@"
die einzige Möglichkeit zu sein, dies zu vermeiden ... (mit,-n1
wenn Sie die Anzahl der Parameter begrenzen möchten))--
die Shell verwendet, um zu sagen, dass keine Optionen mehr akzeptiert werden sollen. Dies ermöglicht es eine sein ,-
nach dem--
zu. Sie können eine sehr interessante und verwirrende Ausgabe erhalten, wenn Sie dies nicht tun, z. B.grep -r
wenn Sie es in das Muster aufnehmen-
! Die Art und Weise, wie Sie es ausdrücken, erklärt dies jedoch nicht wirklich. Iirc, es ist eine POSIX-Sache, aber es lohnt sich trotzdem, darauf hinzuweisen, denke ich. Nur etwas zu beachten. Und ich liebe diesen Bonus übrigens!Dies scheint die sicherste Version zu sein.
(
-0
Kann entfernt und die werdentr
mit einer Umleitung ersetzt (oder die Datei kann mit einem Null - separierte Datei stattdessen ersetzt werden). Es ist vor allem dort , da ich hauptsächlich Verwendungxargs
mitfind
mit-print0
Ausgang) (Dies könnte auch relevant sein , aufxargs
Versionen ohne die-0
Erweiterung)Dies ist sicher, da args die Parameter bei der Ausführung als Array an die Shell übergibt. Die Shell
bash
würde sie dann (zumindest ) als unverändertes Array an die anderen Prozesse übergeben, wenn alle unter Verwendung von erhalten werden["$@"][1]
Wenn Sie verwenden
...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''
, schlägt die Zuweisung fehl, wenn die Zeichenfolge doppelte Anführungszeichen enthält. Dies gilt für jede Variante mit-i
oder-I
. (Da es in eine Zeichenfolge ersetzt wird, können Sie jederzeit Befehle einfügen, indem Sie unerwartete Zeichen (wie Anführungszeichen, Backticks oder Dollarzeichen) in die Eingabedaten einfügen.)Wenn die Befehle jeweils nur einen Parameter annehmen können:
Oder mit etwas weniger Prozessen:
Wenn Sie GNU
xargs
oder eine andere mit der-P
Erweiterung haben und 32 Prozesse parallel ausführen möchten, mit jeweils nicht mehr als 10 Parametern für jeden Befehl:Dies sollte gegenüber Sonderzeichen in der Eingabe robust sein. (Wenn die Eingabe durch Null getrennt ist.) Die
tr
Version erhält eine ungültige Eingabe, wenn einige der Zeilen Zeilenumbrüche enthalten. Dies ist jedoch bei einer durch Zeilenumbrüche getrennten Datei unvermeidbar.Der leere erste Parameter für
bash -c
ist folgendermaßen: (Von derbash
Manpage ) (Danke @clacke)quelle
"$@"
bash -c 'command1 "$@"; command2 "$@";' arbitrarytextgoeshere
bash
with-c
nimmt zuerst (nach den Befehlen) ein Argument, das der Name des Prozesses ist, und nimmt dann die Positionsargumente. Versuchen Sie zubash -c 'echo "$@" ' 1 2 3 4
sehen, was herauskommt.Eine andere mögliche Lösung, die für mich funktioniert, ist so etwas wie -
Beachten Sie die 'Bash' am Ende - ich gehe davon aus, dass sie als argv [0] an Bash übergeben wird. Ohne diese Syntax geht der erste Parameter für jeden Befehl verloren. Es kann ein beliebiges Wort sein.
Beispiel:
quelle
"$@"
, teilen Sie die Argumentliste auf und erweitern sie global.Mein aktuelles BKM dafür ist
Es ist bedauerlich, dass hierfür Perl verwendet wird, das weniger wahrscheinlich installiert wird als Bash. Es werden jedoch mehr Eingaben verarbeitet als die akzeptierte Antwort. (Ich begrüße eine allgegenwärtige Version, die nicht auf Perl basiert.)
@ KeithThompsons Vorschlag von
ist großartig - es sei denn, Sie haben das Shell-Kommentarzeichen # in Ihrer Eingabe. In diesem Fall werden ein Teil des ersten Befehls und der gesamte zweite Befehl abgeschnitten.
Hashes # können häufig vorkommen, wenn die Eingabe aus einer Dateisystemliste wie ls oder find stammt und Ihr Editor temporäre Dateien mit # im Namen erstellt.
Beispiel für das Problem:
Ups, hier ist das Problem:
Ahh, das ist besser:
quelle
ls | xargs -I % sh -c 'echo 1 "%"; echo 2 "%"'