Come fare in modo che `xargs` ignori l'uscita del bambino e continui l'elaborazione


24

A volte svolgo lunghi xargslavori durante la notte ed è davvero fastidioso scoprire al mattino che è xargsmorto da qualche parte nel mezzo, ad esempio a causa di un errore di segmentazione in un singolo caso speciale, come è successo questa notte.

Se anche un xargsbambino viene ucciso, non elabora più input:

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

Posso in qualche modo impedire xargsdi interrompere l'elaborazione di ulteriori input una volta che un processo figlio è morto e invece continuare l'elaborazione?


Sto usando la xargsversione 4.4.2 in debian wheezye sembra che tutto vada bene anche se uccido un sleepprocesso specifico . Quale versione di xargsstai usando? potrebbe essere che hanno risolto il problema nell'ultima versione.
Kannan Mohan,

Un po 'tardi alla festa, ma che ne dici xargs ... bash -c '...;exit 0'o addiritturaxargs ... bash -c '... || echo erk'
Samveen del

Si noti che parallel -j 1è una possibile soluzione di hacking.
Barrycarter

Risposte:


25

No, non puoi. Dalle xargsfonti su 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;

Non c'è bandiera attorno a quel segno di spunta, o attorno alla funzione che lo chiama. Sembra essere correlato a max procs, che suppongo abbia senso: se imposti max procs abbastanza in alto, non si preoccuperà di controllare fino a quando non raggiungerà il limite, cosa che potresti non riuscire mai a fare.

Una soluzione migliore per quello che stai cercando di fare potrebbe essere quella di utilizzare GNU Make :

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

all: $(TARGETS)

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

Poi:

$ make -k -j4 

avrà lo stesso effetto e ti darà un controllo molto migliore.


9

Sembrerebbe che uno dei colloquialismi più ovvi sia accennato solo da altre proposte.

Cioè, è possibile utilizzare quanto segue:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

per "forzare il successo".

Nota, questo è sulla falsariga della proposta di lornix (ma non in così tante parole).

Ad ogni modo, poiché questo sta effettivamente ignorando lo stato di uscita del processo effettivo, mi assicurerei di prendere in considerazione in qualche modo il salvataggio dello stato del processo secondario per l'analisi post mortem. Per esempio:

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

Il truequi è in qualche modo ridondante e quindi questo potrebbe essere meglio scritto come:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Poiché probabilmente vorremmo sapere quando non è stato possibile toccare il file "non riuscito". In altre parole, non stiamo più ignorando il fallimento, stiamo prendendo nota e continuando.

E, dopo aver considerato la natura ricorsiva di questo problema, forse vediamo esattamente perché xargs non rende facile ignorare i fallimenti. Perché non è mai una buona idea, dovresti invece migliorare la gestione degli errori all'interno del processo che stai sviluppando. Credo che questa nozione, tuttavia, sia più inerente alla stessa "filosofia Unix".

Infine, suppongo che sia ciò a cui allude James Youngman raccomandando trap, che presumibilmente potrebbe essere usato in modo simile. Cioè, non ignorare il problema ... intrappolarlo e gestirlo o ti svegli un giorno e scopri che nessuno dei sottoprogrammi è riuscito affatto ;-)


3

Utilizzare 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

In alternativa, passa dalla shell a un'altra lingua in cui puoi anche impostare i gestori di segnale.

Nota anche che dopo bash -c foo..dovresti specificare il valore che $0dovrebbe prendere (qui, fnord) in modo che la prima parola prodotta da seqnon venga mangiata.


2

Inserisci un altro comando lì per "mangiare" il segnale del programma morente.

Ho provato il tuo esempio, inizialmente come mostrato per dimostrare il problema ... "killall sleep" uccide il processo di sleep, interrompe bash e xargs si chiude.

Come test, ho bloccato un comando di tipo 'esegui un altro comando' tra xargs e bash ... in questo caso '/ usr / bin / time'. questa volta (nessun gioco di parole), killall sleep uccide il processo di sleep, ma gli xargs continuano.

Invieresti l'output di time su / dev / null, e questo farebbe esattamente quello che stai cercando senza una grande riscrittura del tuo processo esistente.

Immagino che se riflettessi per un momento, potrei escogitare un altro programma per fare lo stesso senza le chiacchiere stderr di '/ usr / bin / time'. O anche scriverne uno io stesso, è solo un derivato 'fork' (o exec ()).

Ricorda di usare '/ usr / bin / time', poiché non sono sicuro che il 'time' incorporato da bash farà lo stesso 'consumo' del segnale.


1
Una buona alternativa a timequesto scopo sarebbe env, dal momento che tutto ciò che fa è aggiungere zero o più variabili opzionali all'ambiente del programma che esegue. Non emette alcun output e il codice di ritorno del programma richiamato verrà restituito a qualsiasi chiamata env.
James Sneeringer,

{Chuckle} Ci ho pensato un po 'dopo averlo scritto. Il tempo è stata la prima cosa che mi è venuta in mente come comando "esegui qualcosa". Funziona bene però. Complimenti e grazie.
Lornix,

2

timeenvlavorato per me (che passano lungo il valore di ritorno del loro programma figlio) così ho scritto bliss:

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

poi chmod u+x ~/bliss

e qualcosa del genere find_or_similar | xargs ~/bliss fatally_dying_program.sh

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.