Bash: Inicie vários comandos acorrentados em segundo plano
-
03-07-2019 - |
Pergunta
Estou tentando executar alguns comandos em paralelo, em segundo plano, usando o Bash. Aqui está o que estou tentando fazer:
forloop {
//this part is actually written in perl
//call command sequence
print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}
A parte entre os backsticks (``) gera um novo shell e executa os comandos em sucessão. O problema é que o controle do programa original retorna somente após a execução do último comando. Gostaria de executar toda a declaração em segundo plano (não estou esperando nenhum valores de saída/retorno) e gostaria que o loop continuasse em execução.
O programa de chamada (aquele que tem o loop) não terminaria até que todas as conchas geradas terminem.
Eu poderia usar tópicos no Perl para gerar diferentes tópicos que chamam de conchas diferentes, mas parece um exagero ...
Posso iniciar um shell, dar um conjunto de comandos e pedir para ir ao fundo?
Solução
Eu não testei isso, mas que tal
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
Os parênteses significam executar em uma sub -Shell, mas isso não deve doer.
Outras dicas
Obrigado Hugh, isso fez isso:
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
adrianp@frost:~$ stopped
[1]+ Done ( echo "started"; sleep 15; echo "stopped" )
adrianp@frost:~$
As outras idéias não funcionam porque iniciam cada comando em segundo plano, e não a sequência de comando (o que é importante no meu caso!).
Obrigado novamente!
Outra maneira é usar a seguinte sintaxe:
{ command1; command2; command3; } &
wait
Observe que o &
Vai no final do grupo de comando, não após cada comando. O semicolon após o comando final é necessário, assim como o espaço após o primeiro suporte e antes do suporte final. o wait
No final, garante que o processo pai não seja morto antes que o processo infantil gerado (o grupo de comando) termine.
Você também pode fazer coisas sofisticadas como redirecionar stderr
e stdout
:
{ command1; command2; command3; } 2>&2 1>&1 &
Seu exemplo seria:
forloop() {
{ touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
for command in $commands
do
"$command" &
done
wait
O ampers e no final do comando o executa em segundo plano, e o wait
aguarda até que a tarefa em segundo plano seja concluída.
Gavincattell Cheguei ao mais próximo (para Bash, IMO), mas, como Mad_ady apontou, ele não lidava com os arquivos "Lock". Isto deveria:
Se houver outros trabalhos pendentes, o esperar Vou esperar por isso também. Se você precisar esperar por só As cópias, você pode acumular esses PIDs e esperar apenas por eles. Caso contrário, você pode excluir as 3 linhas com "PIDs", mas é mais geral.
Além disso, adicionei verificação para evitar completamente a cópia:
pids=
for file in bigfile*
do
# Skip if file is not newer...
targ=/destination/$(basename "${file}")
[ "$targ" -nt "$file" ] && continue
# Use a lock file: ".fileN.lock" for each "bigfileN"
lock=".${file##*/big}.lock"
( touch $lock; cp "$file" "$targ"; rm $lock ) &
pids="$pids $!"
done
wait $pids
Aliás, parece que você está copiando novos arquivos para um repositório FTP (ou similar). Se sim, você poderia Considere uma estratégia de cópia/renomeação em vez dos arquivos de bloqueio (mas esse é outro tópico).
A instalação em Bash que você está procurando é chamada Compound Commands
. Veja a página do homem para obter mais informações:
Comandos compostos Um comando composto é um dos seguintes:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list. { list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharac‐ ters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace or another shell metacharac‐ ter.
Existem outros, mas esses são provavelmente os dois tipos mais comuns. O primeiro, The Parens, executará uma lista de comando em série em uma subshell, enquanto a segunda, os aparelhos encaracolados, terá uma lista de comandos em série no shell atual.
parens
% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013
aparelho encaracolado
% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
Execute o comando usando um Job:
# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012
O resultado será enviado para sua conta por correio.
Eu tropecei neste tópico aqui e decidi montar um trecho de código para gerar declarações acorrentadas como trabalhos de fundo. Eu testei isso no Bash para Linux, Ksh para IBM Aix e Busybox's Ash para Android, então acho que é seguro dizer que funciona algum Concha semelhante a Bourne.
processes=0;
for X in `seq 0 10`; do
let processes+=1;
{ { echo Job $processes; sleep 3; echo End of job $processes; } & };
if [[ $processes -eq 5 ]]; then
wait;
processes=0;
fi;
done;
Este código executa uma série de trabalhos em segundo plano até um certo limite de trabalhos simultâneos. Você pode usar isso, por exemplo, para recomendar muitos arquivos gzippeados com xz
sem ter um grande monte de xz
Processos comem toda a sua memória e fazem seu computador vomitar: neste caso, você usa *
Enquanto o for
a lista e o trabalho em lote seriam gzip -cd "$X" | xz -9c > "${X%.gz}.xz"
.
Execute os comandos em uma subshell:
(command1 ; command2 ; command3) &
Tente colocar comandos em aparelhos encaracolados com & s, assim:
{command1 & ; command2 & ; command3 & ; }
Isso não cria uma sub-concha, mas executa o grupo de comandos em segundo plano.
Hth
Não sei por que ninguém respondeu com a solução adequada:
my @children;
for (...) {
...
my $child = fork;
exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;
Isso faz com que a Perl gere as crianças que executem cada comando e permite que você espere que todas as crianças se concluam antes de prosseguir.
A propósito,
print `some command`
e
system "some command"
emitir o mesmo conteúdo para stdout, mas o primeiro tem uma sobrecarga mais alta, pois Perl precisa capturar tudo "some command
"SAUT
Bifurcando em um loop:
for i in x; do ((a; b; c;)&); done
Exemplo:
for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done
Apenas para o caso de alguém ainda estar interessado, você pode fazê -lo sem ligar para uma subshell como esta:
print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
Você pode usar gnu parallel
comando para executar trabalhos em paralelo. É mais seguro é mais rápido.
Meu palpite é que você está tentando copiar vários arquivos grandes de origem para destino. E por isso você pode fazer isso em paralelo com a declaração abaixo.
$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'
Como usamos -j0
Opção, todos os arquivos serão copiados em paralelo. Caso você precise reduzir o número de processos paralelos, poderá usar -j<n>
Onde <n>
é o número de processo paralelo a ser executado.
O paralelo também coletará a saída do processo e o relatará de maneira seqüencial (com -k
opção) qual outro mecanismo de controle de trabalho não pode fazer.
--eta
A opção fornecerá as estatísticas de detalhes do processo que está acontecendo. Portanto, podemos saber como maio do processo foi concluído e quanto tempo levará para terminar.