Faça o download de vários arquivos com o wget em paralelo

5

Eu tenho o seguinte script:

for i in {0800..9999}; do
    for j in {001..032}; do
        wget http://example.com/"$i-$j".jpg
    done
done

Todas as fotos existem e, de fato, cada iteração não depende de outra. Como paralelizar com possibilidade de controle do número de threads?

    
por danielleontiev 30.07.2017 / 23:03

4 respostas

18

A resposta da Confiq é boa para os pequenos i e j . No entanto, dado o tamanho de i e j em sua pergunta, você provavelmente desejará limitar o número geral de processos gerados. Você pode fazer isso com o comando parallel ou algumas versões de xargs . Por exemplo, usando um xargs que suporta o -P flag você poderia paralelizar seu loop interno da seguinte forma:

for i in {0800..9999}; do
  echo {001..032} | xargs -n 1 -P 8 -I{} wget http://example.com/"$i-{}".jpg
done

O GNU paralelo possui um grande número de recursos para quando você precisa de um comportamento mais sofisticado e facilita o paralelismo ambos os parâmetros:

parallel -a <(seq 0800 9999) -a <(seq 001 032) -P 8 wget http://example.com/{1}-{2}.jpg
    
por 30.07.2017 / 23:31
3

Aqui está uma abordagem muito simplista. Limita os encadeamentos a 10 neste exemplo.

for i in {0800..9999}; do
    for j in {001..032}; do
        wget http://example.com/"$i-$j".jpg &
        while test $(jobs -p|wc -w) -ge 10; do sleep 0.1 ; done
    done
done
    
por 30.07.2017 / 23:41
3
for i in {1..3}; do
    for j in {10..20}; do
        (wget http://example.com/"$i-$j".jpg &)
    done
done

Eu até testei ...

    
por 30.07.2017 / 23:10
2

Veja mais ou menos como resolvi o mesmo problema em um ambiente em que parallel não estava disponível. Isso depende de recursos bash, então você precisa de #!/bin/bash ou explicitamente executando o script através do bash.

MAX_CONCURRENT=50
n=0
some_command_that_outputs_urls \
| while read url
do
    {
        do_something_with $url
    } &
    PIDS="$PIDS $!"

    ((++n))
    if test "$n" -ge "$MAX_CONCURRENT"
    then
        n=0
        wait $PIDS
        PIDS=""
    fi
done
test -n "$PIDS" && wait $PIDS

Você pode ajustar $MAX_CONCURRENT para especificar o número máximo de segmentos desejado (aproximado). E, claro, você substituirá some_command_that_outputs_urls e do_something_with $url pelo que for apropriado em seu cenário. Por exemplo, você pode substituir a linha some_command_that_outputs_urls \ por

for i in {0800..9999}; do
    for j in {001..032}; do
        printf 'http://example.com/%s-%s.jpg\n' $i $j
    done
done \
# ...| while read url ...

e do_something_with $url simplesmente com

wget $url

dando-lhe o resultado final

MAX_CONCURRENT=50
n=0
for i in {0800..9999}; do
    for j in {001..032}; do
        printf 'http://example.com/%s-%s.jpg\n' $i $j
    done
done \
| while read url
do
    {
        wget $url
    } &
    PIDS="$PIDS $!"

    ((++n))
    if test "$n" -ge "$MAX_CONCURRENT"
    then
        n=0
        wait $PIDS
        PIDS=""
    fi
done
test -n "$PIDS" && wait $PIDS

A maneira como isso funciona é ter um comando que gera a lista de URLs (neste caso) em sua saída padrão, e ler uma linha por vez no loop while (atente para novas linhas!). Ele gerará até $MAX_CONCURRENT processos simultâneos, usando $n para rastrear quantos foram gerados e $PIDS para registrar seus IDs de processo. Uma vez que $MAX_CONCURRENT processos tenham sido gerados (note que o que realmente está sendo criado é uma instrução composta, então você pode ter vários comandos e até mesmo blocos dentro dele), wait nos PIDs gerados (isso retorna imediatamente se nenhum dos PIDs especificados ainda estão sendo executados) e redefinir seu estado interno e, em seguida, prosseguir com outra execução.

Existem várias maneiras de melhorar esse script, incluindo o melhor tratamento de PIDs reutilizadas, mas ele faz o trabalho que eu queria que ele fizesse no ambiente em que ele precisava ser executado, então é bom o suficiente para mim. Na minha versão atual, há também um tempo limite no local e ele é executado novamente regularmente por meio do cron, portanto, o risco de tempos de execução descontrolados é muito reduzido em comparação com essa versão mais simples.

    
por 31.07.2017 / 13:28