Eu tenho um script bash parecido com o seguinte:
##script #!/bin/bash rm data* rm logfile* for i in {1..30} do ## append a & if you want to run it parallel; nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" & done
Eu gostaria de criar outro loop for após o primeiro para continuar por mais 30. Por exemplo
##script #!/bin/bash rm data* rm logfile* for i in {1..30} do ## append a & if you want to run it parallel; nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" & for i in {31..60} do ## append a & if you want to run it parallel; nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" & done
Eu gostaria de o primeiro conjunto de trabalhos a terminar antes de iniciar o novo conjunto. Mas por causa do nohup
, parece que todos eles são executados simultaneamente.
Eu tenho nohup
porque eu loguei remotamente em meu servidor e iniciei os trabalhos lá e então fechei meu bash. Existe uma solução alternativa?
Comentários
Resposta
Você vai querer usar o comando wait
para faça isso por você. Você pode capturar todos os IDs de processos filhos e esperar por eles especificamente ou, se eles forem os únicos processos em segundo plano que seu script está criando, basta chamar wait
sem um argumento. Por exemplo:
#!/bin/bash # run two processes in the background and wait for them to finish nohup sleep 3 & nohup sleep 10 & echo "This will wait until both are done" date wait date echo "Done"
Resposta
Alguns pontos:
-
Se seu objetivo com
nohup
é evitar que uma saída de shell remota mate seus processos de trabalho, você deve usarnohup
no próprio script, não nos processos de trabalho individuais que ele cria. -
Conforme explicado aqui ,
nohup
apenas impede que os processos recebam SIGHUP e de interagir com o terminal, mas não quebra a relação entre o shell e seus processos filho. -
Por causa do ponto acima, com ou sem
nohup
, um simpleswait
entre os doisfor
loops causará o segundofor
para ser executado somente depois que todos os processos filho iniciados pelo primeirofor
tenham saído. -
Com um
wait
:todos os processos filho ativos atualmente são aguardados e o status de retorno é zero.
-
Se você precisar executar o segundo
for
apenas se não houver erros no primeiro , então você precisará salvar cada PID de trabalhador com$!
e passá-los todos parawait
:pids= for ... worker ... & pids+=" $!" done wait $pids || { echo "there were errors" >&2; exit 1; }
Comentários
- Pode haver Seriam outros trabalhos em execução no servidor. Então, eu ' d quero apenas aguardar meu lote .. eles são scripts R, portanto, são executados em
R
oucc1plus
notop
comando - Também eu ' d gostaria de usar nohup dentro para executar todos os comandos em " paralelo ". basicamente, são simulações para um programa científico. Quero executar 180 simulações no total, mas em lotes de 60. O contador também precisa ir de 1 a 180. Se eu fizer uma de cada vez, vai demorar muito.
-
wait
faz com quebash
espere pelos trabalhos de segundo plano que ele mesmo gerou, nada mais. Pode haver alguma confusão aqui- estesfor
loops, você os salvou em um arquivo e os chamou como um script (o que presumi, por causa do##script
linha), ou você está digitando-os manualmente no terminal? - Eu estava fazendo
cat file.txt | while
e o pids não foi definido fora do loop portanto, o comando de espera viu uma string$pids
vazia. por que isso acontece é discutido em serverfault.com/q/259339 . facilmente corrigido comowhile ... < files.txt
conforme respondido em serverfault.com/a/259346 - Apenas curioso qual é a finalidade do sinal + com a variável pids?
Resposta
Use o fg
integrado. Ele aguarda até que os processos em segundo plano terminem.
Tente help fg
para obter detalhes.
Comentários
- Um script é executado sem controle de trabalho.
Resposta
Se você inserir algo como o seguinte segmento de código entre seus dois for
loops, pode ajudar.
flag=0 while [ flag -eq 0 ] do ps -ef | grep "Rscript --vanilla" | grep -v grep > /dev/null flag=${?} sleep 10 done
Claro, se seu aplicativo Rscript
tem uma chance de não completar com sucesso e demorar, seu segundo loop for pode não ter a chance de rodar. O segmento de código acima pressupõe que todos os processos com o identificador Rscript --vanilla
serão concluídos e desaparecerão corretamente. Sem saber o que seu aplicativo faz e como funciona, tenho que confiar nessa suposição.
EDITAR
À luz dos comentários, isso seria melhor suas necessidades. (inclui seu código original, bem como a lógica de verificação de conclusão)
for i in {1..30} do ## append a & if you want to run it parallel; nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" & pids[$i]=${!} done flag=0 while [ flag -eq 0 ] do for PID in $(echo ${pids[@]}) do flag=1 ps -ef | grep ${PID} | grep -v grep >/dev/null; r=${?} if [ ${r} -eq 0 ] then flag=0 fi done done for i in {31..60} do ## append a & if you want to run it parallel; nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" & done
Comentários
- O processo nome em
top
mostraR
às vezes oucc1plus
. - Nesse caso, você precisará encontrar um denominador comum, aparecendo na lista
ps -ef
. Ou depois de cada comandonohup
, registre o PID em uma variável (de preferência uma matriz) porecho ${!}
e verifique este grupo de PIDs. Quando todos eles desaparecerem, você pode prosseguir para o segundofor
loop
wait
integrado.