Pergunta

Qual é a melhor/mais fácil forma de criar um mínimo de tarefa de sistema de fila para Linux com bash e ferramentas comuns?

Eu tenho um arquivo com 9'000 linhas, cada linha tem um comando de linha de comando, os comandos são completamente independentes.

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
...

Minha caixa tem mais de um núcleo e quero executar X tarefas ao mesmo tempo.Eu procurei na web para uma boa maneira de fazer isso.Aparentemente, muitas pessoas têm este problema, mas ninguém tem uma boa solução até agora.

Seria bom se a solução tinha as seguintes características:

  • pode interpretar mais de um comando (por exemplo, command; command)
  • pode interpretar o fluxo de redirecionamentos nas linhas (por exemplo, ls > /tmp/ls.txt)
  • utiliza apenas ferramentas comuns no Linux

Pontos de bônus se ele funciona em outros Unix-clones sem muito exótica requisitos.

Foi útil?

Solução

Você pode converter sua lista de comando em um Makefile? Nesse caso, você pode simplesmente executar "Make -j X".

Outras dicas

GNU paralelo http://www.gnu.org/software/parallel/ é uma ferramenta mais geral para paralelizante que o PPSS.

Se o Runfile contiver:

command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log

você pode fazer:

cat runfile | parallel -j+0

que executará um comando por núcleo da CPU.

Se seus comandos forem tão simples quanto acima, você nem precisará de runfile, mas pode fazer:

seq 1 3 | parallel -j+0 'command {} > Logs/{}.log'

Se você tiver mais computadores disponíveis para fazer o processamento, poderá olhar para as opções - -SSHLOGIN e - -TRC para o GNU paralelo.

Ok, depois de postar a pergunta aqui, encontrei o seguinte projeto que parece promissor: PPSS.

EDIT: Não é exatamente o que eu quero, o PPSS está focado no processamento de "todos os arquivos no diretório A".

Bem, este é um tipo de diversão questão de qualquer maneira.

Aqui está o que eu faria, assumindo bash(1) do curso.

  • descobrir como muitos destes comandos pode ser útil executar em simultâneo.Não vai ser apenas o número de núcleos;um monte de comandos será suspenso e/S e esse tipo de coisa.Ligar para o número N. N=15 por exemplo.
  • montar uma armadilha processador de sinal para o sinal SIGCHLD, que ocorre quando uma criança de processo termina. trap signalHandler SIGCHLD
  • cat sua lista de comandos dentro de um tubo
  • escrever um ciclo que se lê do stdin e executa os comandos, um por um, diminuindo um contador.Quando o contador for 0, waits.
  • seu processador de sinal, que é executado no SIGCHLD, incrementos de esse contador.

Então, agora, corre-se o primeiro N comandos e, em seguida, aguarda.Quando o primeiro filho termina, a espera de retorno, lê-se outra linha, é executado um comando novo, e espera novamente.

Agora, este é um caso que cuida de muitos trabalhos para terminar juntos.Eu suspeito você pode começar com uma versão mais simples:

 N=15
 COUNT=N
 cat mycommands.sh | 
 while read cmd 
 do
   eval $cmd &
   if $((count-- == 0))
   then
       wait
   fi
 od

Agora, este irá iniciar os primeiros 15 comandos e, em seguida, execute o resto uma vez que alguns de comando termina.

Diversão de computação distribuída semelhante é o script MapReduce Bash:

http://blog.last.fm/2009/04/06/mapreduce-bash-script

E obrigado por apontar o PPSS!

Você pode usar o Xargs comando, é -Max-Procs faz o que você quer. Por exemplo, a solução Charlie Martin se torna com XARGS:

tr '\012' '\000' <mycommands.sh |xargs --null --max-procs=$X bash -c

detalhes:

  • X é o número de processos máx. Por exemplo: x = 15. -Max-Procs está fazendo a mágica
  • O primeiro TR está aqui para rescindir linhas por bytes nulos para XARGS -NULL opção para que as citações de redirecionamento etc.
  • Bash -C executa o comando

Eu testei com este arquivo myCommands.sh, por exemplo:

date
date "+%Y-%m-%d" >"The Date".txt
wc -c <'The Date'.txt >'The Count'.txt

Este é um caso específico, mas se você estiver tentando processar um conjunto de arquivos e produzir outro conjunto de arquivos de saída, poderá iniciar o número de processos #Cores e verificar se existe um arquivo de saída antes de processá -lo. O exemplo abaixo converte um diretório de arquivos .m4b em arquivos .mp3:

Basta executar este comando quantas vezes você tem núcleos:

ls *m4b | enquanto lido f; Teste -f $ {f%m4b} mp3 || mencoder -Of Rawaudio "$ f" -oac mp3Lame -ovc cópia -o $ {f%m4b} mp3; feito &

Você pode ver minha fila de tarefas escritas no Bash: https://github.com/pavelpat/yastq

Fila de tarefas + paralelo + adição dinâmica

Usando um FIFO, esse script em si para processar a fila. Dessa forma, você pode adicionar comandos à fila em tempo real (quando a fila já estiver iniciada).

Uso: ./Queue Command [# of Children] [Nome da fila

Exemplo, com 1 thread:

./queue "sleep 5; echo ONE"
./queue "echo TWO"

Resultado:

ONE
TWO

Exemplo, com 2 thread:

./queue "sleep 5; echo ONE" 2
./queue "echo TWO"

Resultado:

TWO
ONE

Exemplo, com 2 filas:

./queue "sleep 5; echo ONE queue1" 1 queue1
./queue "sleep 3; echo ONE queue2" 1 queue2

Resultado:

ONE queue2
ONE queue1

O script (salve -o como "fila" e chmod +x fila):

    #!/bin/bash

    #Print usage
    [[ $# -eq 0 ]] && echo Usage: $0 Command [# of children] [Queue name] && exit

    #Param 1 - Command to execute
    COMMAND="$1"

    #Param 2 - Number of childs in parallel
    MAXCHILD=1
    [[ $# -gt 1 ]] && MAXCHILD="$2"

    #Param 3 - File to be used as FIFO
    FIFO="/tmp/defaultqueue"
    [[ $# -gt 2 ]] && FIFO="$3"

    #Number of seconds to keep the runner active when unused
    TIMEOUT=5

    runner(){
      #Associate file descriptor 3 to the FIFO
      exec 3"$FIFO"

      while read -u 3 -t $TIMEOUT line; do
        #max child check
        while [ `jobs | grep Running | wc -l` -ge "$MAXCHILD" ]; do
          sleep 1
        done

        #exec in backgroud
        (eval "$line")&
      done
      rm $FIFO
    }

    writer(){
      #fork if the runner is not running
      lsof $FIFO >/dev/null || ($0 "QueueRunner" "$MAXCHILD" "$FIFO" &)

      #send the command to the runner
      echo "$COMMAND" > $FIFO
    }

    #Create the FIFO file
    [[ -e "$FIFO" ]] || mkfifo "$FIFO"

    #Start the runner if in the runner fork, else put the command in the queue
    [[ "$COMMAND" == "QueueRunner" ]] && runner || writer

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top