Shell-Funktionen mit xargs aufrufen


168

Ich versuche, xargs zu verwenden, um eine komplexere Funktion parallel aufzurufen.

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
seq -f "n%04g" 1 100 |xargs -n 1 -P 10 -i echo_var {} 
exit 0

Dies gibt den Fehler zurück

xargs: echo_var: No such file or directory

Ideen, wie ich xargs verwenden kann, um dies zu erreichen, oder andere Lösungen, wären willkommen.


2
Gefahr, user1148366, Gefahr! Verwenden Sie bash nicht für die parallele Programmierung - Sie werden auf so viele Probleme stoßen. Verwenden Sie C / C ++ und pthreads oder Java-Threads oder alles, was Sie dazu bringt, lange und gründlich darüber nachzudenken, was Sie tun, da die parallele Programmierung viel Nachdenken erfordert, um richtig zu werden.
David Souther

27
@DavidSouther Wenn die Aufgaben unabhängig sind, z. B. alle diese Bilddateien in PNG konvertieren, machen Sie sich keine Sorgen. Wenn Sie eine Synchronisierung (über das Warten hinaus, bis alle fertig sind) und Kommunikation haben, wird es chaotisch.
Strg-Alt-Delor

@DavidSouther - Ich bin ein langjähriger Java-Entwickler und habe in letzter Zeit in Groovy gearbeitet. Und ich sage den Leuten weiterhin: Freunde lassen Freunde kein Bash-Skript schreiben. Und doch sehe ich mir diesen Beitrag / diese Lösung an, weil ich (trauriges Gesicht :() mit paralleler Verarbeitung in Bash beschäftigt bin. Ich könnte es leicht in Groovy / Java tun. Schlecht!
Christian Bongiorno

Antworten:


172

Das Exportieren der Funktion sollte dies tun (ungetestet):

export -f echo_var
seq -f "n%04g" 1 100 | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

Sie können das eingebaute printfanstelle des externen verwenden seq:

printf "n%04g\n" {1..100} | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

Durch die Verwendung von return 0und auf exit 0diese Weise wird auch jeder Fehlerwert maskiert, der möglicherweise durch den vorhergehenden Befehl erzeugt wird. Wenn es keinen Fehler gibt, ist dies die Standardeinstellung und daher etwas redundant.

@phobic erwähnt, dass der Bash-Befehl vereinfacht werden könnte

bash -c 'echo_var "{}"'

Bewegen Sie das {}direkt hinein. Es ist jedoch anfällig für Befehlsinjektionen, wie von @Sasha hervorgehoben.

Hier ist ein Beispiel, warum Sie das eingebettete Format nicht verwenden sollten :

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "{}"'
Sun Aug 18 11:56:45 CDT 2019

Ein weiteres Beispiel dafür, warum nicht :

echo '\"; date\"' | xargs -I {} bash -c 'echo_var "{}"'

Dies wird im sicheren Format ausgegeben :

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "$@"' _ {}
$(date)

Dies ist vergleichbar mit der Verwendung parametrisierter SQL- Abfragen , um eine Injektion zu vermeiden .

Ich verwende hier dateeine Befehlsersetzung oder in Anführungszeichen anstelle des rmBefehls, der in Sashas Kommentar verwendet wird, da er nicht destruktiv ist.


14
Ein bisschen mehr Diskussion: xargs führt eine völlig neue Instanz des genannten Prozesses aus. In diesem Fall geben Sie den Namen an echo_var, der eine Funktion in diesem Skript und kein Prozess (Programm) in Ihrem PATH ist. Die Lösung von Dennis besteht darin, die Funktion für untergeordnete Bash-Prozesse zu exportieren, sie dann in den Unterprozess zu verschieben und dort auszuführen.
David Souther

7
Was ist die Bedeutung von _und \ohne sie hat es bei mir nicht funktioniert
Hashbrown

9
@Hashbrown: Der Unterstrich ( _) bietet einen Platzhalter für argv[0]( $0) und fast alles kann dort verwendet werden. Ich glaube, ich habe das Backslash-Semikolon ( \;) hinzugefügt, weil es zum Beenden der -execKlausel in verwendet wird find, aber es funktioniert hier ohne es. Wenn die Funktion $@stattdessen verwendet $1würde, würde sie das Semikolon als Parameter sehen, daher sollte es weggelassen werden.
Bis auf weiteres angehalten.

4
-i Argument zu xargs ist seitdem veraltet. Verwenden Sie stattdessen -I (Kapital i).
Nicolai S

11
Sie können dies vereinfachen, indem Sie das Argument von xargs in die Befehlszeichenfolge für bash with aufnehmen bash -c 'echo_var "{}"'. Sie brauchen also am Ende nicht das _ {}.
Phobie

16

Die Verwendung von GNU Parallel sieht folgendermaßen aus:

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
export -f echo_var
seq -f "n%04g" 1 100 | parallel -P 10 echo_var {} 
exit 0

Wenn Sie die Version 20170822 verwenden, müssen Sie dies nicht einmal tun, solange Sie Folgendes ausgeführt export -fhaben:

. `which env_parallel.bash`
seq -f "n%04g" 1 100 | env_parallel -P 10 echo_var {} 

wo bekomme ich shopt für osx?
Nick

nvm es ist setopt in zsh
Nick

Dies wird unten angezeigt. Fehler Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: parallel_bash_environment: Zeile 79: Syntaxfehler: unerwartetes Dateiende sh: Fehler beim Importieren der Funktionsdefinition für parallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' / usr / local / bin / bash: parallel_bash_environment: Zeile 79: Syntaxfehler: unerwartetes Ende von file / usr / local / bin / bash: Fehler beim Importieren der Funktionsdefinition für `...
Nick

Sie wurden geschockt: Shellshock hatte keinen direkten Einfluss auf GNU Parallel. Die Lösung für Shellshock war jedoch: Es hat --env und den env_parallel-Trick völlig gebrochen. Es wird angenommen, dass es in der Git-Version behoben ist
Ole Tange

1
Ich mag diese Antwort, weil ich dadurch das parallele Tool entdeckt habe
JR Utily

10

So etwas sollte auch funktionieren:

function testing() { sleep $1 ; }
echo {1..10} | xargs -n 1 | xargs -I@ -P4 bash -c "$(declare -f testing) ; testing @ ; echo @ "

1

Möglicherweise ist dies eine schlechte Vorgehensweise, aber wenn Sie Funktionen in einem .bashrcoder einem anderen Skript definieren, können Sie die Datei oder zumindest die Funktionsdefinitionen mit der Einstellung allexport:

set -o allexport

function funcy_town {
  echo 'this is a function'
}
function func_rock {
  echo 'this is a function, but different'
}
function cyber_func {
  echo 'this function does important things'
}
function the_man_from_funcle {
  echo 'not gonna lie'
}
function funcle_wiggly {
  echo 'at this point I\'m doing it for the funny names'
}
function extreme_function {
  echo 'goodbye'
}

set +o allexport
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.