Wie man `xargs` veranlasst, den Ausgang des Kindes zu ignorieren und die Verarbeitung fortzusetzen

24

Ich habe manchmal über xargsNacht lange Jobs und es ist wirklich ärgerlich, am Morgen herauszufinden, dass ich xargsirgendwo in der Mitte gestorben bin , zum Beispiel wegen eines Segmentierungsfehlers in einem einzigen Sonderfall, wie es diese Nacht passiert ist.

Wenn auch nur ein xargsKind getötet wird, werden keine Eingaben mehr verarbeitet:

Konsole 1:

[09:35:48] % seq 40 | xargs -i --max-procs=4 bash -c 'sleep 10; date +"%H:%M:%S {}";'
xargs: bash: terminated by signal 15
09:35:58 3
09:35:58 4
09:35:58 2
<Exit with code 125>

Konsole 2:

[09:35:54] kill 5601

Kann ich irgendwie verhindern xargs, dass die Verarbeitung von Eingaben unterbrochen wird, sobald ein untergeordneter Prozess stirbt, und stattdessen die Verarbeitung fortsetzen?

Christoph Wurm
quelle
Ich verwende xargsVersion 4.4.2 in debian wheezyund es sieht so aus, als ob alles gut läuft, auch wenn ich einen bestimmten sleepProzess beende. Welche Version von verwendest xargsdu? Möglicherweise haben sie das Problem in der neuesten Version behoben.
Kannan Mohan
Ein bisschen zu spät zur Party, aber wie wäre es xargs ... bash -c '...;exit 0'oder sogarxargs ... bash -c '... || echo erk'
Samveen
Beachten Sie, dass dies parallel -j 1eine mögliche Hack-Lösung ist.
barrycarter

Antworten:

25

Nein, kannst du nicht. Aus den xargsQuellen bei savannah.gnu.org :

if (WEXITSTATUS (status) == CHILD_EXIT_PLEASE_STOP_IMMEDIATELY)
  error (XARGS_EXIT_CLIENT_EXIT_255, 0,
         _("%s: exited with status 255; aborting"), bc_state.cmd_argv[0]);
if (WIFSTOPPED (status))
  error (XARGS_EXIT_CLIENT_FATAL_SIG, 0,
         _("%s: stopped by signal %d"), bc_state.cmd_argv[0], WSTOPSIG (status));
if (WIFSIGNALED (status))
  error (XARGS_EXIT_CLIENT_FATAL_SIG, 0,
         _("%s: terminated by signal %d"), bc_state.cmd_argv[0], WTERMSIG (status));
if (WEXITSTATUS (status) != 0)
  child_error = XARGS_EXIT_CLIENT_EXIT_NONZERO;

Es gibt kein Flag um diese Prüfung oder um die Funktion, die sie aufruft. Es scheint mit max procs zu tun zu haben, was meiner Meinung nach Sinn macht: Wenn Sie max procs hoch genug einstellen, wird es nicht die Mühe machen zu prüfen, bis es das Limit erreicht, was Sie vielleicht nie erreichen werden.

Eine bessere Lösung für das, was Sie versuchen, könnte die Verwendung von GNU Make sein :

TARGETS=$(patsubst %,target-%,$(shell seq 1 40))

all: $(TARGETS)

target-%:
    sleep 10; date +"%H:%M:%S $*"

Dann:

$ make -k -j4 

wird den gleichen Effekt haben und Ihnen eine viel bessere Kontrolle geben.

ckhan
quelle
9

Es scheint, dass einer der offensichtlichsten Umgangssprachen nur von anderen Vorschlägen angedeutet wird.

Das heißt, Sie können Folgendes verwenden:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

um "Erfolg zu erzwingen".

Beachten Sie, dass dies im Sinne des Vorschlags von Lornix ist (nur nicht in so vielen Worten).

Da dies den tatsächlichen Status des Prozessabbruchs effektiv ignoriert, sollten Sie in Betracht ziehen, den Status des Unterprozesses für die Post-Mortem-Analyse zu speichern. Z.B:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed; (true)'

Das truehier ist etwas überflüssig und daher könnte dies besser geschrieben werden als:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Da würden wir wahrscheinlich gerne wissen, wann die 'fehlgeschlagene' Datei nicht angerührt werden konnte. Mit anderen Worten, wir ignorieren den Fehler nicht länger , wir nehmen ihn zur Kenntnis und fahren fort.

Und nachdem wir uns die rekursive Natur dieses Problems angesehen haben, sehen wir vielleicht genau, warum es mit xargs nicht einfach ist, Fehler zu ignorieren. Da dies keine gute Idee ist, sollten Sie stattdessen die Fehlerbehandlung in dem von Ihnen entwickelten Prozess verbessern. Ich glaube jedoch, dass dieser Begriff eher der "Unix-Philosophie" selbst innewohnt.

Schließlich ist es wohl auch das, worauf James Youngman mit einer Empfehlung anspielt trap, die vermutlich auf ähnliche Weise verwendet werden könnte. Das heißt, ignorieren Sie das Problem nicht ... fangen Sie es ein und behandeln Sie es, oder Sie wachen eines Tages auf und stellen fest, dass keines der Unterprogramme überhaupt erfolgreich war ;-)

kingofephyra
quelle
3

Verwendung trap:

$ seq 40 | xargs -i --max-procs=4 bash -c \
 'trap "echo erk; exit 1" INT TERM;  sleep 10; date +"%H:%M:%S {}";' fnord
16:07:39 2
16:07:39 4
erk
16:07:39 1
^C
erk
erk
erk
erk

Wechseln Sie alternativ von der Shell in eine andere Sprache, in der Sie auch Signalhandler festlegen können.

Beachten Sie auch, dass bash -c foo..Sie nach dem Festlegen des Werts $0(hier, fnord) festlegen sollten, dass das erste von erzeugte Wort seqnicht gegessen wird.

James Youngman
quelle
2

Geben Sie einen weiteren Befehl ein, um das Signal aus dem sterbenden Programm zu "essen".

Ich habe zunächst Ihr Beispiel ausprobiert, um das Problem zu beweisen ... 'killall sleep' bricht den Schlafprozess ab, unterbricht bash und xargs wird beendet.

Als Test habe ich einen Befehl vom Typ 'run another command' zwischen xargs und bash eingefügt ... in diesem Fall '/ usr / bin / time'. Dieses Mal (kein Wortspiel) beendet killall sleep den Schlafprozess, aber die Xargs gehen weiter.

Sie würden die Ausgabe der Zeit nach / dev / null leiten, und dies würde genau das tun, wonach Sie suchen, ohne Ihren vorhandenen Prozess grundlegend umzuschreiben.

Ich stelle mir vor, wenn ich für einen Moment nachdenke, könnte ich mir ein anderes Programm einfallen lassen, um dasselbe zu tun, ohne das stderr-Chatter von '/ usr / bin / time'. Oder schreibe selbst einen, es ist nur eine Gabel (oder exec () - Ableitung).

Denken Sie daran, '/ usr / bin / time' zu verwenden, da ich nicht sicher bin, ob die eingebaute 'time' von bash dasselbe 'eating' des Signals bewirkt.

Lornix
quelle
1
Eine gute Alternative zu timediesem Zweck wäre, dass envder Umgebung des ausgeführten Programms lediglich null oder mehr optionale Variablen hinzugefügt werden. Es gibt keine eigene Ausgabe aus, und der Rückgabecode des aufgerufenen Programms wird an das aufgerufene Programm zurückgegeben env.
James Sneeringer
{Lacht} Ich habe eine Weile darüber nachgedacht, nachdem ich das aufgeschrieben habe. Die Zeit war das erste, was mir als "etwas ausführen" -Befehl in den Sinn kam. Funktioniert aber gut. Herzlichen Glückwunsch und vielen Dank.
Lornix
2

Weder für mich timenoch envfür mich gearbeitet (sie geben den Rückgabewert ihres Kinderprogramms weiter), also schrieb ich bliss:

#!/bin/sh
"$@"
exit 0

dann chmod u+x ~/bliss

und so ähnlich find_or_similar | xargs ~/bliss fatally_dying_program.sh

pix
quelle