it-swarm.com.de

Aufruf mehrerer Befehle über xargs

cat a.txt | xargs -I % echo %

Im obigen Beispiel verwendet xargs echo % als Befehlsargument. In einigen Fällen benötige ich jedoch mehrere Befehle, um das Argument anstelle eines Befehls zu verarbeiten. Zum Beispiel:

cat a.txt | xargs -I % {command1; command2; ... }

Xargs akzeptiert dieses Formular jedoch nicht. Eine Lösung, die ich kenne, ist, dass ich eine Funktion definieren kann, die die Befehle umgibt, aber es ist keine Pipeline, ich ziehe es nicht vor. Gibt es eine andere Lösung?

240
Dagang
cat a.txt | xargs -I % sh -c 'command1; command2; ...'

Beachten Sie, dass dies eine nutzlose Verwendung von cat ist. Ich würde es schreiben als:

< a.txt xargs -I % sh -c 'command1; command2; ...'

(Ja, die Umleitung kann am Anfang des Befehls stehen.)

Vermutlich wird command1 und/oder command2 ein oder mehrere % Zeichen enthalten; Andernfalls wäre die Option -I % für xargs nicht viel wert.

360
Keith Thompson

Mit GNU Parallel können Sie Folgendes tun:

cat a.txt | parallel 'command1 {}; command2 {}; ...; '

In den Intro-Videos erfahren Sie mehr: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

27
Ole Tange

Dies ist nur ein weiterer Ansatz ohne xargs oder cat:

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt
23
hmontoliu

Sie können verwenden

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'

{} = Variable für jede Zeile in der Textdatei

16
Ossama

Eine Sache, die ich mache, ist, diese Funktion zu .bashrc/.profile hinzuzufügen:

function each() {
    while read line; do
        for f in "[email protected]"; do
            $f $line
        done
    done
}

dann kannst du so etwas machen

... | each command1 command2 "command3 has spaces"

was weniger ausführlich ist als xargs oder -exec. Sie können die Funktion auch so ändern, dass der Wert an einer beliebigen Stelle in die Befehle eingefügt wird, wenn Sie dieses Verhalten auch benötigen.

14
mwm

Ich bevorzuge einen Stil, der den Trockenlaufmodus (ohne | sh) erlaubt:

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

Funktioniert auch mit Pfeifen:

cat a.txt | xargs -I % echo "echo % | cat " | sh
9
brablc

Ein bisschen zu spät zur Party. 

Ich verwende das folgende Format zum Komprimieren meiner Verzeichnisse mit Tausenden kleiner Dateien vor der Migration. Wenn Sie keine einfachen Anführungszeichen in Befehlen benötigen, sollte dies funktionieren.

Mit einigen Modifikationen bin ich sicher, dass es für jemanden nützlich sein wird. Getestet in Cygwin (babun)

find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'

find . Hier finden
-maxdepth 1 Gehen Sie nicht in untergeordnete Verzeichnisse
! -path . Ausschließen./Aktueller Verzeichnispfad
-type d stimmen nur mit Verzeichnissen überein
-print0 Ausgabe getrennt durch Nullbytes\0
| xargs Pipe to xargs
-0 Die Eingabe ist durch Leerzeichen getrennte Bytes
-I @@ Platzhalter ist @@. Ersetzen Sie @@ durch Eingabe.
bash -c '...' Ausführen des Bash-Befehls
{...} Befehlsgruppierung
&& Nächsten Befehl nur ausführen, wenn vorheriger Befehl erfolgreich beendet wurde (exit 0) 

Final ; ist wichtig, sonst schlägt es fehl.

Ausgabe:

Completed compressing directory ./Directory1 with meta characters in it
Completed compressing directory ./Directory2 with meta characters in it
Completed compressing directory ./Directory3 with meta characters in it

Aktualisierung im Juli 2018:

Wenn Sie Hacks lieben und herumspielen, ist hier etwas interessantes:

echo "a b c" > a.txt
echo "123" >> a.txt
echo "###this is a comment" >> a.txt
cat a.txt
myCommandWithDifferentQuotes=$(cat <<'EOF'                                     
echo "command 1: [email protected]"; echo 'will you do the fandango?'; echo "command 2: [email protected]"; echo
EOF
)
< a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@

Ausgabe:

command 1: a b c
will you do the fandango?
command 2: a b c

command 1: 123
will you do the fandango?
command 2: 123

command 1: ###this is a comment
will you do the fandango?
command 2: ###this is a comment

Erläuterung:
- Erstellen Sie ein Single-Liner-Skript und speichern Sie es in einer Variablen
- xargs liest a.txt und führt es als bash-Skript aus
- @@ stellt sicher, dass eine ganze Zeile übergeben wird
- Wenn @@ hinter -- gestellt wird, wird sichergestellt, dass @@ als Positionsparametereingabe für den Befehl bash verwendet wird, nicht als bash start OPTION, d. H. Wie -c selbst, was run command bedeutet.

-- ist magisch und funktioniert mit vielen anderen Dingen, d. h. ssh, sogar kubectl

6
sdkks

Eine andere mögliche Lösung, die für mich funktioniert, ist etwa

cat a.txt | xargs bash -c 'command1 [email protected]; command2 [email protected]' bash

Beachten Sie das 'bash' am Ende - ich gehe davon aus, dass es 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:

cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " [email protected]; echo "data again: " [email protected]' bash
6
tavvit

Meine derzeitige BKM dafür ist

... | xargs -n1 -I % Perl -e 'system("echo 1 %"); system("echo 2 %");'

Es ist bedauerlich, dass Perl verwendet wird, das weniger wahrscheinlich als bash installiert wird. aber es verarbeitet mehr Eingabe als die akzeptierte Antwort. (Ich begrüße eine allgegenwärtige Version, die nicht auf Perl angewiesen ist.)

@ KeithThompsons Vorschlag von 

 ... | xargs -I % sh -c 'command1; command2; ...'

ist großartig - es sei denn, Sie haben das Shell-Kommentarzeichen # in Ihrer Eingabe. In diesem Fall werden ein Teil des ersten Befehls und aller zweiten Befehle abgeschnitten.

Hashes # können durchaus üblich sein, wenn die Eingabe aus einer Dateisystemliste wie ls oder find stammt und Ihr Editor temporäre Dateien mit dem Namen # erstellt.

Beispiel für das Problem:

$ bash 1366 $>  /bin/ls | cat
#Makefile#
#README#
Makefile
README

Ups, hier ist das Problem:

$ bash 1367 $>  ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README

Ahh, das ist besser:

$ bash 1368 $>  ls | xargs -n1 -I % Perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>  
1
Krazy Glew

Dies scheint die sicherste Version zu sein.

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "[email protected]"; command2 "[email protected]";'

(-0 kann entfernt und die tr durch eine Weiterleitung ersetzt werden (oder die Datei kann stattdessen durch eine durch Leerzeichen getrennte Datei ersetzt werden.) Dies ist hauptsächlich darin enthalten, da ich hauptsächlich xargs mit find mit -print0-Ausgabe verwende xargs Versionen ohne die Erweiterung -0)

Dies ist sicher, da args die Parameter als Array an die Shell übergibt, bevor sie ausgeführt wird. Die Shell (zumindest bash) übergibt sie dann als unverändertes Array an die anderen Prozesse, wenn alle mit ["[email protected]"][1] abgerufen werden.

Wenn Sie ...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' verwenden, schlägt die Zuweisung fehl, wenn die Zeichenfolge doppelte Anführungszeichen enthält. Dies gilt für jede Variante, die -i oder -I verwendet. (Da er in einen String umgewandelt wird, können Sie immer Befehle einfügen, indem Sie unerwartete Zeichen (wie Anführungszeichen, Backticks oder Dollarzeichen) in die Eingabedaten einfügen.

Wenn die Befehle nur jeweils einen Parameter annehmen können:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "[email protected]"; command2 "[email protected]";'

Oder mit etwas weniger Prozessen:

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "[email protected]"; do command1 "$f"; command2 "$f"; done;'

Wenn Sie GNU xargs oder eine andere mit der Erweiterung -P haben und Sie möchten 32 Prozesse parallel ausführen, von denen jeder nicht mehr als 10 Parameter für jeden Befehl hat:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "[email protected]"; command2 "[email protected]";'

Dies sollte gegenüber allen Sonderzeichen in der Eingabe robust sein. (Wenn die Eingabe durch Null getrennt ist.) Die tr-Version wird einige ungültige Eingaben erhalten, wenn einige Zeilen Zeilenumbrüche enthalten. Dies ist jedoch bei einer durch Zeilenumbrüche getrennten Datei unvermeidbar.

0