Como fazer 'xargs' ignorar a saída da criança e continuar processando

21

Eu às vezes executo longos trabalhos de xargs durante a noite e é realmente irritante descobrir pela manhã que xargs morreu em algum lugar no meio, por exemplo por causa de uma falha de segmentação em um único caso especial, como aconteceu esta noite.

Se até mesmo um xargs child for eliminado, ele não processará mais nenhuma entrada:

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 de alguma forma impedir que xargs pare de processar mais alguma entrada quando um processo filho for interrompido e continuar processando?

    
por Christoph Wurm 05.07.2012 / 09:42

5 respostas

23

Não, você não pode. Das xargs sources em 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;

Não há sinalizador em torno desse cheque ou da função que o chama. Parece estar relacionado ao max procs, o que eu acho que faz sentido: se você definir o max procs alto o suficiente, ele não vai se incomodar em verificar até que ele esteja no limite, o que você pode ser nunca.

Uma solução melhor para o que você está tentando fazer é usar o GNU Make :

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

all: $(TARGETS)

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

Então:

$ make -k -j4 

terá o mesmo efeito e lhe dará um controle muito melhor.

    
por 05.07.2012 / 11:02
7

Parece que um dos coloquialismos mais óbvios é apenas aludido por outras propostas.

Ou seja, você pode usar o seguinte:

bash -c '$PROG_WHICH_MAY_FAIL ; (true)'

para "forçar o sucesso".

Observe que isso se enquadra na proposta da lornix (apenas não em tantas palavras).

De qualquer forma, como isso está efetivamente ignorando o status real de saída do processo, garanto que você considere de alguma forma salvar o status do subprocesso para análise post-mortem. Por exemplo:

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

O true aqui é um pouco redundante e, portanto, pode ser melhor escrito como:

bash -c '$PROG_WHICH_MAY_FAIL || touch failed'

Como provavelmente gostaríamos de saber quando o arquivo "com falha" não pôde ser tocado. Em outras palavras, não ignoramos mais a falha, estamos anotando e continuando.

E, depois de considerar a natureza recursiva dessa questão, talvez vejamos exatamente por que o xargs não torna fácil ignorar a falha. Como isso nunca é uma boa ideia - você deve estar aprimorando o tratamento de erros no processo que está desenvolvendo. Acredito que essa noção, no entanto, é mais inerente à "filosofia Unix" em si.

Finalmente, suponho que isso também é o que James Youngman alude ao recomendar trap , que presumivelmente poderia ser usado de maneira semelhante. Isto é, não ignore o problema ... prenda-o e manuseie-o ou acordará um dia e descobrirá que nenhum dos subprogramas foi bem-sucedido; -)

    
por 18.09.2014 / 20:18
3

Use 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

Alternativamente, alterne do shell para outro idioma no qual você também pode definir manipuladores de sinal.

Note também que após bash -c foo.. você deve especificar o valor que $0 deve tomar (aqui, fnord ) para que a primeira palavra produzida por seq não seja comida.

    
por 06.07.2012 / 17:09
2

Coloque outro comando lá para "comer" o sinal do programa que está morrendo.

Eu tentei o seu exemplo, inicialmente como mostrado para provar o problema ... 'killall sleep' mata o processo de suspensão, interrompe o bash e o xargs termina.

Como teste, eu coloquei um comando do tipo 'execute outro comando' entre xargs e bash ... neste caso '/ usr / bin / time'. desta vez (sem trocadilho), o sono matinal mata o processo do sono, mas os xargs continuam.

Você canalizaria a saída do tempo para / dev / null, e isso faria exatamente o que você está procurando sem uma grande reescrita do processo existente.

Eu imagino que se eu refletir por um momento, eu poderia criar outro programa para fazer o mesmo sem a tagarelice stderr de '/ usr / bin / time'. Ou até mesmo escrever um, é apenas um 'fork' (ou derivado exec ()).

Lembre-se de usar '/ usr / bin / time', pois não tenho certeza se o 'time' embutido do bash fará o mesmo 'comer' do sinal.

    
por 05.07.2012 / 20:46
1

Nem time nem env funcionaram para mim (eles transmitem o valor de retorno de seu programa filho), então eu escrevi bliss :

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

então chmod u+x ~/bliss

e algo parecido com find_or_similar | xargs ~/bliss fatally_dying_program.sh

    
por 19.12.2013 / 02:53