Eu tenho um comando chamado CMD do meu script principal do bourne que leva uma eternidade.
Quero modificar o script da seguinte maneira:
- Execute o comando CMD em paralelo como um processo em segundo plano (
CMD &
). - No script principal, tenha um loop para monitorar o comando gerado a cada poucos segundos. O loop também ecoa algumas mensagens para stdout indicando o progresso do script.
- Saia do loop quando o comando gerado terminar.
- Capture e relate o código de saída do processo gerado.
Alguém pode me dar dicas para fazer isso?
Respostas:
1: No bash,
$!
mantém o PID do último processo em segundo plano que foi executado. Isso vai te dizer qual processo monitorar, de qualquer maneira.4:
wait <n>
aguarda até que o processo com PID<n>
seja concluído (ele será bloqueado até a conclusão do processo, portanto, talvez você não queira chamá-lo até ter certeza de que o processo está concluído) e, em seguida, retornará o código de saída do processo concluído.2, 3:
ps
oups | grep " $! "
pode dizer se o processo ainda está em execução. Depende de você entender o resultado e decidir o quão próximo ele está do acabamento. (ps | grep
não é à prova de idiotas. Se você tiver tempo, poderá encontrar uma maneira mais robusta de saber se o processo ainda está em execução).Aqui está um script esqueleto:
fonte
ps -p $my_pid -o pid=
nemgrep
é necessário.kill -0 $!
é a melhor maneira de saber se um processo ainda está em execução. Na verdade, ele não envia nenhum sinal, apenas verifica se o processo está ativo, usando um shell embutido em vez de processos externos. Comoman 2 kill
diz: "Se sig for 0, nenhum sinal será enviado, mas a verificação de erros ainda será executada; isso pode ser usado para verificar a existência de um ID do processo ou do grupo de processos".kill -0
retornará diferente de zero se você não tiver permissão para enviar sinais para um processo em execução. Infelizmente, ele retorna1
nesse caso e no caso em que o processo não existe. Isso o torna útil, a menos que você não seja o proprietário do processo - o que pode ser o caso mesmo dos processos criados por você, se uma ferramentasudo
estiver envolvida ou se eles estiverem configurados (e possivelmente descartarem privs).wait
não retorna o código de saída na variável$?
. Ele apenas retorna o código de saída e$?
é o código de saída do último programa em primeiro plano.kill -0
. Aqui está uma referência revisada por pares da SO que mostra que o comentário de CraigRinger é legítimo em relação a:kill -0
retornará diferente de zero para processos em execução ... masps -p
sempre retornará 0 para qualquer processo em execução .Foi assim que resolvi quando tive uma necessidade semelhante:
fonte
wait
s faz com que o script aguarde até o final de (cada) processo.O pid de um processo filho em segundo plano é armazenado em $! . Você pode armazenar todos os pids dos processos filhos em uma matriz, por exemplo, PIDS [] .
Aguarde até que o processo filho especificado por cada ID de processo pid ou especificação de tarefa jobspec saia e retorne o status de saída do último comando esperado. Se uma especificação de trabalho for fornecida, todos os processos no trabalho serão aguardados. Se nenhum argumento for fornecido, todos os processos filhos ativos no momento serão aguardados e o status de retorno será zero. Se a opção -n for fornecida, aguarde o término de qualquer trabalho e retorne seu status de saída. Se nem jobspec nem pid especificarem um processo filho ativo do shell, o status de retorno será 127.
Use o comando wait, você pode esperar pelo término de todos os processos filhos, enquanto isso, você pode obter o status de saída de cada processo filho via $? e armazene o status no STATUS [] . Então você pode fazer algo dependendo do status.
Eu tentei as 2 soluções a seguir e elas funcionam bem. solution01 é mais conciso, enquanto solution02 é um pouco complicado.
solution01
solution02
fonte
pid=$!; PIDS[$i]=${pid}; ((i+=1))
pode ser escrito de maneira mais simples, como oPIDS+=($!)
que simplesmente se anexa à matriz sem precisar usar uma variável separada para indexação ou o próprio pid. O mesmo se aplica àSTATUS
matriz.Como vejo quase todas as respostas, use utilitários externos (principalmente
ps
) para pesquisar o estado do processo em segundo plano. Existe uma solução mais unixesh, captando o sinal SIGCHLD. No manipulador de sinal, é necessário verificar qual processo filho foi parado. Isso pode ser feitokill -0 <PID>
embutido (universal) ou verificando a existência de/proc/<PID>
diretório (específico do Linux) ou usando ojobs
embutido (festançaespecífico.jobs -l
também relata o pid. Nesse caso, o terceiro campo da saída pode ser Parado | Em execução | Concluído | Sair. )Aqui está o meu exemplo.
O processo iniciado é chamado
loop.sh
. Ele aceita-x
ou um número como argumento. For-x
é saídas com o código de saída 1. Para um número, espera num * 5 segundos. A cada 5 segundos, ele imprime seu PID.O processo do iniciador é chamado
launch.sh
:Para obter mais explicações, consulte: Falha ao iniciar um processo a partir do script bash
fonte
for i in ${!pids[@]};
usando a expansão de parâmetros.fonte
grep -v
. Você pode limitar a pesquisa ao início da linha:grep '^'$pid
além disso, você pode fazer isso deps p $pid -o pid=
qualquer maneira. Além disso,tail -f
não vai terminar até você matá-lo, então não acho que seja uma maneira muito boa de demonstrar isso (pelo menos sem apontar isso). Você pode redirecionar a saída do seups
comando para/dev/null
ou ela será exibida na tela a cada iteração. Suasexit
causaswait
são ignoradas - provavelmente deve serbreak
. Mas não são oswhile
/ps
e oswait
redundantes?kill -0 $pid
? Na verdade, ele não envia nenhum sinal, apenas verifica se o processo está ativo, usando um shell embutido em vez de processos externos.bash: kill: (1) - Operation not permitted
Eu mudaria sua abordagem um pouco. Em vez de verificar a cada poucos segundos se o comando ainda está ativo e relatar uma mensagem, tenha outro processo que relate a cada poucos segundos que o comando ainda está sendo executado e depois elimine esse processo quando o comando terminar. Por exemplo:
fonte
while kill -0 $pid 2> /dev/null; do X; done
, espero que seja útil para alguém no futuro que lê esta mensagem;)Nossa equipe tinha a mesma necessidade de um script remoto executado por SSH que atingia o tempo limite após 25 minutos de inatividade. Aqui está uma solução com o loop de monitoramento verificando o processo em segundo plano a cada segundo, mas imprimindo apenas a cada 10 minutos para suprimir um tempo limite de inatividade.
fonte
Um exemplo simples, semelhante às soluções acima. Isso não requer o monitoramento de nenhuma saída do processo. O próximo exemplo usa cauda para seguir a saída.
Use tail para seguir a saída do processo e saia quando o processo estiver concluído.
fonte
Outra solução é monitorar processos através do sistema de arquivos proc (mais seguro que o ps / grep combo); quando você inicia um processo, ele tem uma pasta correspondente em / proc / $ pid, para que a solução possa ser
Agora você pode usar a variável $ exit_status como quiser.
fonte
Syntax error: "else" unexpected (expecting "done")
Com esse método, seu script não precisa aguardar o processo em segundo plano, você precisará monitorar apenas um arquivo temporário para o status de saída.
agora, seu script pode fazer qualquer outra coisa enquanto você apenas precisa monitorar o conteúdo do retFile (ele também pode conter qualquer outra informação que você desejar, como o horário de saída).
PS .: btw, eu codifiquei o pensamento no bash
fonte
Isso pode estar além da sua pergunta, no entanto, se você estiver preocupado com o tempo de execução dos processos, talvez esteja interessado em verificar o status da execução de processos em segundo plano após um intervalo de tempo. É fácil verificar quais PIDs filhos ainda estão sendo executados
pgrep -P $$
, no entanto, eu vim com a seguinte solução para verificar o status de saída desses PIDs que já expiraram:quais saídas:
Nota: Você pode mudar
$pids
para uma variável de sequência em vez de matriz para simplificar as coisas, se quiser.fonte
Minha solução foi usar um canal anônimo para passar o status para um loop de monitoramento. Não há arquivos temporários usados para trocar status, nada para limpar. Se você não tiver certeza sobre o número de trabalhos em segundo plano, pode ser a condição de interrupção
[ -z "$(jobs -p)" ]
.fonte