Como executar comandos como em uma fila


42

Eu preciso fazer muitas cópias de vários arquivos em várias pastas. Posso adicionar todos os meus comandos de cópia a um script bash e executá-lo, mas devo esperar até que termine se desejar adicionar mais comandos à "fila" de cópia.

Existe uma maneira de executar comandos como uma fila e adicionar mais comandos a essa fila enquanto as coisas estão em execução?

Explicado de uma maneira diferente, quero iniciar uma tarefa de longa duração. Enquanto isso estiver em execução, quero iniciar outro que não seja iniciado até que o primeiro seja concluído. E depois adicione outro após o último e assim por diante. Isso é possível de alguma forma?


3
Posso sugerir que você aceite uma resposta que resolva seu problema. Parece que existem alguns deles que funcionariam para você.
holmb

2
Sim, você pode, e eu queria. O problema é que perguntei isso quando trabalhei em uma empresa que usava Macs. Não estou mais trabalhando lá, nem tenho Macs, portanto não tenho como testar nenhum deles. Não se sinta à vontade para marcar uma como resposta quando eu não conseguir testar se ela realmente funciona, então, por enquanto, espero que as pessoas votem nas respostas que acharem úteis para elas, para que a "resposta" possa surgir dessa maneira. em vez de.
Svish

Respostas:


25

O atutilitário, mais conhecido por executar comandos em um horário especificado, também possui um recurso de fila e pode ser solicitado a iniciar a execução de comandos agora. Ele lê o comando para executar a partir da entrada padrão.

echo 'command1 --option arg1 arg2' | at -q myqueue now
echo 'command2 ...' | at -q myqueue now

O batchcomando é equivalente a at -q b -m now(o que -msignifica que a saída do comando, se houver, será enviada para você, como o cron). Nem todas as variantes do unix suportam nomes de fila ( -q myqueue); você pode estar limitado a uma única fila chamada b. O Linux até limitado a nomes de fila de uma letra.


2
No Linux, pelo menos, isso não parece esperar o término do comando anterior.
Wds

@wds Funciona para mim no Debian chiado. Onde e como isso aconteceu para você? Observe que aguarda apenas o comando terminar; se o comando lança subprocessos que sobrevivem aos pais, at não espera pelos subprocessos.
Gilles 'SO- stop be evil'

Eu tentei no CentOS 6. Testei com apenas um simples "sono x". Não sei o que fazer para executar isso, mas presumo que ele inicia um subshell e esse subshell deve aguardar o resultado (uma chamada de suspensão não retorna imediatamente).
wds 17/02/2015

Bem como os nomes das filas, 'agora' não aparece padrão ou: bash no ubuntu está reclamando sobre isso ...
Winwaed

@winwaed Oh, nownão -t now, desculpe. Eu não tinha notado que estava errado no código de exemplo.
Gilles 'SO- stop be evil'

23

Anexe &ao final do seu comando para enviá-lo para segundo plano e, waitem seguida , antes de executar o próximo. Por exemplo:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...

2
yay! esta sintaxe é incrível quando você chutou algo fora e depois ir olhando para stackoverflow uma maneira de fazer fila algo depois
Erik Aronesty

2
Se você mudar de idéia sobre algum desses comandos nesse meio tempo, o que pode ser feito para abortar wait? Parece impermeável a todas as minhas mortes.
Ken Williams

1
Você acabou de matar o comando. O waitsimplesmente pára o trabalho até que os anteriores terminar.
Gert Sønderby:

Isso vai funcionar nohup?
Michael

11

As soluções de Brad e Mankoff são boas sugestões. Outro que é semelhante a uma combinação dos dois seria usar o GNU Screen para implementar sua fila. Isso tem a vantagem de poder ser executado em segundo plano, você pode verificá-lo sempre que, e enfileirar novos comandos apenas os cola no buffer para serem executados após a saída dos comandos anteriores.

Primeira corrida:

$ screen -d -m -S queue

(aliás, agora é uma boa hora para brincar com alguns arquivos impressionantes. screenrc )

Isso gerará uma sessão de tela em segundo plano para você chamada fila.

Agora, coloque na fila quantos comandos desejar:

screen -S queue -X stuff "echo first; sleep 4; echo second^M"

Estou executando vários comandos no exemplo acima apenas para teste. Seu caso de uso provavelmente seria mais parecido com:

screen -S queue -X stuff "echo first^M"
screen -S queue -X stuff "echo second^M"

Observe que o "^ M" na minha linha acima é uma maneira de obter uma nova linha incorporada que será interpretada posteriormente depois que a tela a inserir no shell bash existente. Use "CTL-V" para obter essa sequência.

Seria muito fácil criar alguns scripts shell simples para automatizar isso e enfileirar comandos. Em seguida, sempre que desejar verificar o status da sua fila de segundo plano, reconecte-o via:

screen -S queue -r

Tecnicamente, você nem precisa nomear sua sessão de tela e ela funcionará bem, mas depois que você ficar viciado nela, você vai querer deixar uma funcionando o tempo todo. ;-)

Obviamente, se você fizer isso, outra boa maneira de fazer isso seria nomear uma das janelas atuais como "fila" e usar:

screen -S queue -p queue -X stuff "command"

Parece que isso basicamente apenas "digita" o comando na sessão da tela em execução, de modo que quando o shell obtém o controle novamente após o término do primeiro comando, esse comando será iniciado. Em outras palavras, é equivalente à solução do @ mankoff, mas usa uma ferramenta mais sofisticada para digitar.
Ken Williams

Praticamente - a principal diferença é que a solução de tela suporta melhor automação. Você pode fazer algumas coisas manualmente, outras coisas com scripts, tudo com uma interface simples.
Jordan

@ Jordan Muito bom, funciona para mim. Mas o que é "material" para depois de -X?
Konstantin

@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - coloque o que segue no terminal como se você o digitasse)
Jordânia

@ Jordan Oh, eu vejo agora. É um comando. Eu pensei que é uma corda arbitrária.
Konstantin

8

Existe um utilitário que usei com grande sucesso exatamente para o caso de uso que você está descrevendo. Recentemente, mudei meu laptop principal para um novo hardware, que envolvia a transferência de alguns arquivos para um NAS e o restante para a nova máquina.

Foi assim que eu fiz.

  1. Configure todas as máquinas envolvidas com uma conexão de rede para que elas possam se alcançar.

  2. Na máquina de onde você está movendo os arquivos (a seguir denominada máquina de origem), instale o rsync com apt-get install rsynco spooler de tarefas de molho secreto (site http://vicerveza.homeunix.net/~viric/soft/ts/ ). Se você está no Debian, o nome do pacote tsé task-spoolere o executável é renomeado tsppara evitar conflitos de nomes com o tsexecutável do moreutilspacote. O pacote Debian vinculado a partir do site é perfeitamente instalável no Ubuntu com dpkg -i task-spooler_0.7.3-1_amd64.debou similar.

  3. Verifique também se todas as máquinas possuem SSH instalado apt-get install openssh-server. Na máquina de origem, você precisa configurar o SSH para permitir o login sem senha nas máquinas de destino. O método mais utilizado será a autenticação de chave pública com ssh-agent(consulte https://www.google.se/search?q=ssh+public+key+authentication para exemplos disso), mas às vezes uso um método mais fácil que funciona apenas também com autenticação de senha. Adicione o seguinte à configuração do cliente SSH ( ~/.ssh/configou /etc/ssh/ssh_config):

    Host *
         ControlMaster auto
         ControlPath ~/.ssh/master-%r@%h:%p
    

    Em seguida, abra um terminal na máquina de origem, efetue login ssh target1, autentique como de costume e deixe esse terminal aberto. Observe que existe um arquivo de soquete nomeado ~/.ssh/master-user@target1:22, este é o arquivo que manterá aberta uma sessão mestre autenticada e permitirá conexões sem senha subseqüentes por user(desde que a conexão use o mesmo nome de host e porta de destino).

    Nesse ponto, você precisa verificar se é possível fazer login sem ser solicitado a autenticação nas máquinas de destino.

  4. Agora execute ts rsync -ave ssh bigfile user@target1:um único arquivo ou ts rsync -ave ssh bigdir user@target1:um diretório. Com o rsync, é importante não incluir uma barra no diretório (em bigdircomparação com bigdir/) ou o rsync assumirá que você quis dizer o equivalente bigdir/*na maioria das outras ferramentas.

    O spooler de tarefas retornará o prompt e permitirá enfileirar muitos desses comandos em sucessão. Inspecione a fila de execução tssem argumentos.

O spooler de tarefas possui muitos recursos, como reorganizar a fila de execução, executar um trabalho específico apenas se outro trabalho foi executado com êxito, etc. Veja a ajuda com ts -h. Às vezes, inspeciono a saída do comando enquanto ele está sendo executado ts -c.

Existem outros métodos para fazer isso, mas para o seu caso de uso, todos incluem o spooler de tarefas. Eu escolhi usar o rsync sobre SSH para preservar os registros de data e hora do arquivo, que não seriam copiados pelo SMB.


obrigado por sua resposta que eu não conhecia ts, parece muito poderoso e fácil de usar, apenas bifurcado no github, autotoolizado e debianizado.
Alex

@Alex Eu dei uma olhada no seu fork e fiquei especialmente interessado no que foi feito no que diz respeito ao autotooling e debianizing da fonte original, e olhando para os seus commits, não estava prontamente disponível como um diferencial para a fonte original. Você se importaria em forçar o envio de novos commit que, na etapa 1., importam a fonte original e 2. comprometem-se com suas adições? Isso ajudará alguém a navegar na sua fonte a (mais facilmente) confiar em suas adições à fonte original. Este é um dos poucos pacotes que eu não instalo a partir de um PPA ou similar, portanto seria bom construí-lo pessoalmente com o seu fork.
holmb

você sabe que tenho que trocar de óculos, perdi a parte da sua resposta sobre o existente task-spooler:-) ... enfim, isso no seu comentário é uma sugestão muito boa, farei muito em breve.
Alex

feito, apagados e re-criou o repositório com commits progressistas, apenas um par de erros (deve metabolizar commits mais locais antes de push)
Alex

4

Também preciso de coisas como essa com bastante frequência. Escrevi um pequeno utilitário chamado afterque executa um comando sempre que outro processo é concluído. Se parece com isso:

#!/usr/bin/perl

my $pid = shift;
die "Usage: $0 <pid> <command...>" unless $pid =~ /^\d+$/ && @ARGV;

print STDERR "Queueing process $$ after process $pid\n";
sleep 1 while -e "/proc/$pid";
exec @ARGV;

Você então executa da seguinte maneira:

% command1 arg1 arg2 ...  # creates pid=2853
% after 2853 command2 arg1 arg2 ... # creates pid=9564
% after 9564 command3 arg1 arg2 ...

A grande vantagem disso em relação a outras abordagens é que o primeiro trabalho não precisa ser executado de nenhuma maneira especial; você pode acompanhar qualquer processo com seu novo trabalho.


Bom script @ken. Embora eu recomendo experimentar o spooler de tarefas, pois você parece exigir isso de tempos em tempos. Veja minha resposta.
holmb

Parece limpo, obrigado. A única coisa que falta no TS parece ser a capacidade de seguir trabalhos que não foram iniciados no TS. Embora eu ache que você possa iniciar um novo trabalho de TS, cujo objetivo é apenas aguardar a conclusão de um processo existente.
21713 Ken Williams

De fato. Esse é um aspecto útil da sua solução.
holmb

2

Command1 && Command2

  • o "&&" significa que o comando2 é executado apenas após o comando1 terminar com um código de retorno 0
  • Nota: a || significa que o comando2 é executado somente após o comando1 terminar com um código de retorno Diferente de 0

1

Você pode simplesmente adicionar comandos à linha de comando do shell que já está executando um comando.

Digite com cuidado ou em outro lugar, verifique e recorte e cole. Mesmo que o comando atual esteja exibindo linhas de saída, você ainda pode digitar algo novo, pressionar enter e ele será executado quando todos os comandos em execução ou digitados antes de serem concluídos.

O exemplo a seguir. Você pode recortar e colar a coisa toda ou inserir os comandos subseqüentes enquanto o primeiro estiver em execução (se você digitar realmente muito rápido), ou mais provavelmente enquanto o segundo estiver em execução .:

echo "foo"
sleep 10; echo "bar"
sleep 3
echo "baz"

0

a maneira mais hacki em que consigo pensar é manter a fila "state" com arquivos de bloqueio simples em / tmp. quando file1.sh estiver executando seus comandos cp, ele manterá um arquivo de bloqueio (ou hardlink) em / tmp. Quando terminar, apague o arquivo. todos os outros scripts terão que procurar em / tmp arquivos de bloqueio com o esquema de nomenclatura que você escolher. naturalmente, isso está sujeito a todos os tipos de falhas, mas é trivial de configurar e é factível completamente dentro do bash.


Difícil de usar na prática, pois exige que todo trabalho que você deseja executar precise ser modificado para manter os arquivos de bloqueio e precise saber (ou aceitar como parâmetros) quais arquivos de bloqueio serão usados. Sempre que possível, é melhor ter a fila externa ao trabalho.
Ken Williams
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.