Comment faire pour que `xargs` ignore la sortie de l'enfant et continue le traitement

24

Je fais parfois de longs xargstravaux pendant la nuit et c'est vraiment ennuyeux de découvrir le matin qui est xargsmort quelque part au milieu, par exemple à cause d'une erreur de segmentation dans un seul cas spécial, comme cela s'est produit cette nuit.

Si même un xargsenfant est tué, il ne traite plus d'entrée:

Console 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>

Console 2:

[09:35:54] kill 5601

Puis-je empêcher en quelque sorte xargsde s'arrêter pour traiter plus de données une fois qu'un processus enfant est mort et continuer à la place?

Christoph Wurm
la source
J'utilise la xargsversion 4.4.2 debian wheezyet il semble que tout fonctionne bien même si je tue un sleepprocessus spécifique . Quelle version xargsutilisez-vous? peut-être qu'ils ont résolu le problème dans la dernière version.
Kannan Mohan,
Un peu tard pour la fête, mais que diriez-vous xargs ... bash -c '...;exit 0'ou mêmexargs ... bash -c '... || echo erk'
Samveen
Notez que parallel -j 1c'est une solution de piratage possible.
barrycarter

Réponses:

25

Non, tu ne peux pas. Des xargssources à 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;

Il n'y a aucun indicateur autour de cette vérification ou autour de la fonction qui l'appelle. Cela semble être lié à max procs, ce qui, je suppose, a du sens: si vous définissez max procs suffisamment haut, cela ne vous dérangera pas de vérifier jusqu'à ce qu'il atteigne la limite, ce que vous pourriez ne jamais être.

Une meilleure solution pour ce que vous essayez de faire pourrait être d'utiliser GNU Make :

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

all: $(TARGETS)

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

Ensuite:

$ make -k -j4 

aura le même effet et vous donnera un bien meilleur contrôle.

ckhan
la source
9

Il semblerait que l'une des expressions les plus évidentes ne soit évoquée que par d'autres propositions.

Autrement dit, vous pouvez utiliser les éléments suivants:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

afin de "forcer le succès".

Notez que cela va dans le sens de la proposition de lornix (mais pas en autant de mots).

Quoi qu'il en soit, étant donné que cela ignore effectivement l'état de sortie du processus réel, je m'assurerais que vous envisagiez d'enregistrer le statut de sous-processus pour l'analyse post mortem. Par exemple:

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

Le trueici est quelque peu redondant et cela peut donc être mieux écrit comme:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Puisque nous aimerions probablement savoir quand le fichier «en échec» n'a pas pu être touché. Autrement dit, nous ne sommes plus ignorer l'échec, nous prenons note et continue.

Et, après avoir considéré la nature récursive de ce problème, nous voyons peut-être exactement pourquoi xargs ne facilite pas l'ignorance de l'échec. Parce que ce n'est jamais une bonne idée - vous devriez plutôt améliorer la gestion des erreurs dans le processus que vous développez. Je crois cependant que cette notion est plus inhérente à la "philosophie Unix" elle-même.

Enfin, je suppose que c'est aussi à cela que James Youngman fait allusion en recommandant trap, ce qui pourrait vraisemblablement être utilisé d'une manière similaire. Autrement dit, n'ignorez pas le problème ... piègez-le et gérez-le ou vous vous réveillez un jour et constatez qu'aucun des sous-programmes n'a réussi du tout ;-)

kingofephyra
la source
3

Utilisation 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

Vous pouvez également passer du shell à une autre langue dans laquelle vous pouvez également définir des gestionnaires de signaux.

Notez également qu'après bash -c foo..vous devez spécifier la valeur qui $0devrait prendre (ici, fnord) afin que le premier mot produit par seqne soit pas mangé.

James Youngman
la source
2

Mettez une autre commande là-dedans pour «manger» le signal du programme mourant.

J'ai essayé votre exemple, initialement comme indiqué pour prouver le problème ... 'killall sleep' tue le processus de sommeil, interrompt bash et xargs se ferme.

Comme test, j'ai coincé une commande de type 'exécuter une autre commande' entre xargs et bash ... dans ce cas '/ usr / bin / time'. cette fois (sans jeu de mots), killall sleep tue le processus de sommeil, mais les xargs continuent.

Vous dirigeriez la sortie du temps vers / dev / null, et cela ferait exactement ce que vous recherchez sans une réécriture majeure de votre processus existant.

J'imagine que si je réfléchis un instant, je pourrais trouver un autre programme pour faire la même chose sans le bavardage stderr de '/ usr / bin / time'. Ou même en écrire un moi-même, c'est juste un 'fork' (ou un dérivé exec ()).

N'oubliez pas d'utiliser «/ usr / bin / time», car je ne suis pas sûr que le «temps» intégré de bash fera la même «consommation» du signal.

lornix
la source
1
Une bonne alternative à timecet effet serait env, car il ne fait qu'ajouter zéro ou plusieurs variables facultatives à l'environnement du programme qu'il exécute. Il n'émet aucune sortie propre et le code retour du programme appelé sera retransmis à l'appel env.
James Sneeringer
{Chuckle} J'y ai pensé un moment après avoir écrit ceci. Le temps était la première chose qui me vint à l'esprit comme une commande «exécuter quelque chose». Fonctionne bien cependant. Félicitations et merci.
lornix
2

Ni timeni envtravaillé pour moi (ils transmettent la valeur de retour de leur programme enfant) alors j'ai écrit bliss:

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

puis chmod u+x ~/bliss

et quelque chose comme find_or_similar | xargs ~/bliss fatally_dying_program.sh

pix
la source