Executando programas em paralelo usando xargs

86

Atualmente tenho o script atual.

#!/bin/bash
# script.sh

for i in {0..99}; do
   script-to-run.sh input/ output/ $i
done

Desejo executá-lo em paralelo usando xargs. eu tentei

script.sh | xargs -P8

Mas fazer o que precede só é executado uma vez de cada vez. Sem sorte com -n8 também. Adicionar & no final da linha a ser executada no script para loop tentaria executar o script 99 vezes de uma vez. Como faço para executar o loop apenas 8 de cada vez, até 100 no total.

Olivier
fonte
Isso é o que eu inicialmente queria fazer, mas tive que recorrer ao xargs porque estou no Windows. Não consegui fazer o GNU Parallel rodando no Windows
Olivier
Esse script está chamando a si mesmo ou você apenas confundiu os nomes quando perguntou aqui?
Etan Reisner
Desculpe, deveria chamar outro script. Vou consertar
Olivier
A resposta para stackoverflow.com/questions/3321738/… é relevante aqui.
Etan Reisner

Respostas:

129

Na xargspágina de manual:

Esta página de manual documenta a versão GNU do xargs. xargs lê itens da entrada padrão, delimitados por espaços em branco (que podem ser protegidos com aspas duplas ou simples ou uma barra invertida) ou novas linhas e executa o comando (o padrão é / bin / echo) uma ou mais vezes com quaisquer argumentos iniciais seguidos por itens lidos da entrada padrão. As linhas em branco na entrada padrão são ignoradas.

O que significa que, para o seu exemplo, xargsestá esperando e coletando toda a saída do seu script e depois executando echo <that output>. Não é exatamente tão útil nem o que você queria.

O -nargumento é quantos itens da entrada usar com cada comando executado (nada, por si só, sobre paralelismo aqui).

Para fazer o que quiser, xargsvocê precisa fazer algo mais parecido com isto (não testado):

printf %s\\n {0..99} | xargs -n 1 -P 8 script-to-run.sh input/ output/

Que quebra assim.

  • printf %s\\n {0..99}- Imprima um número por linha de 0a 99.
  • Corre xargs
    • pegando no máximo um argumento por linha de comando de execução
    • e executar até oito processos por vez
Etan Reisner
fonte
8
Na verdade, você não precisa colocar os argumentos em linhas separadas; xargs divisões de palavras. Então echo {0..99} |funcionaria tão bem. <<<{0..99}não parece funcionar; embora <<<wordseja documentado como uma palavra expansível de chave, isso não acontece com nenhuma versão do bash que tenho em mãos.
rici
1
@rici Parece um bug de documentação, especialmente porque a documentação de Here Documents não menciona expansão de chaves (e isso não acontece lá também em um teste rápido) embora também não mencionem a expansão de til (o que não acontece para <<, mas faz por <<<assim *shrug*). As expansões que acontecem e não acontecem aqui docs e aqui strings são um pouco estranhas para mim.
Etan Reisner
1
Como você pode separar resultados de diferentes execuções com, por exemplo, novas linhas?
nirvana-msu
3
Demo: time head -12 <(yes "1") | xargs -n1 -P4 sleepexecutará 12 sleep 1comandos, 4 paralelos. O comando demorará 3 segundos.
Walter A
66

Com o GNU Parallel você faria:

parallel script-to-run.sh input/ output/ {} ::: {0..99}

Adicione -P8se você não quiser executar um trabalho por núcleo da CPU.

O oposto xargsfará a coisa certa, mesmo se a entrada contiver espaço, ', ou "(não é o caso aqui, embora). Ele também garante que a saída de diferentes trabalhos não sejam misturados, então se você usar a saída, garantindo que você não obterá meia linha de dois trabalhos diferentes

GNU Parallel é um paralelizador geral e facilita a execução de trabalhos em paralelo na mesma máquina ou em várias máquinas às quais você tem acesso ssh.

Se você tiver 32 tarefas diferentes que deseja executar em 4 CPUs, uma maneira direta de paralelizar é executar 8 tarefas em cada CPU:

Agendamento simples

Em vez disso, o GNU Parallel gera um novo processo quando um termina - mantendo as CPUs ativas e, portanto, economizando tempo:

Programação GNU Parallel

Instalação

Se GNU Parallel não estiver empacotado para sua distribuição, você pode fazer uma instalação pessoal, que não requer acesso root. Isso pode ser feito em 10 segundos fazendo o seguinte:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 67bd7bc7dc20aff99eb8f1266574dadb
12345678 67bd7bc7 dc20aff9 9eb8f126 6574dadb
$ md5sum install.sh | grep b7a15cdbb07fb6e11b0338577bc1780f
b7a15cdb b07fb6e1 1b033857 7bc1780f
$ sha512sum install.sh | grep 186000b62b66969d7506ca4f885e0c80e02a22444
6f25960b d4b90cf6 ba5b76de c1acdf39 f3d24249 72930394 a4164351 93a7668d
21ff9839 6f920be5 186000b6 2b66969d 7506ca4f 885e0c80 e02a2244 40e8a43f
$ bash install.sh

Para outras opções de instalação, consulte http://git.savannah.gnu.org/cgit/parallel.git/tree/README

Saber mais

Veja mais exemplos: http://www.gnu.org/software/parallel/man.html

Assista aos vídeos de introdução: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Percorra o tutorial: http://www.gnu.org/software/parallel/parallel_tutorial.html

Inscreva-se na lista de e-mail para obter suporte: https://lists.gnu.org/mailman/listinfo/parallel

Ole Tange
fonte
19
Isso não responde à pergunta, nem indica por que xargs não pode alcançar a mesma coisa.
张 实 唯
8
downvote porque xarg para mim faz exatamente como mostra a segunda imagem.
noonex
3
@noonex Você está ciente de que nem todo mundo usa a versão do xargs que você usa e que -P não está em todas as versões do xargs?
Ole Tange
20
Talvez nem todos estejam cientes de que esta resposta é fornecida pelo autor do GNU parallel.
Izkeros de
1
Votos negados devido a um anúncio claro em um software que não funciona corretamente conforme descrito nas primeiras tentativas, devido a um prompt interativo que bagunça a maioria dos scripts.
Daniel Sorichetti