web-dev-qa-db-ja.com

wgetを使用して複数のファイルを並行してダウンロードする

次のbashスクリプトがあります。

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

すべての写真が存在し、実際、各反復は別の反復に依存していません。スレッド数を制御できるように並列化するにはどうすればよいですか?

7
danielleontiev

Confiqの答えは、小さいijに適しています。ただし、質問でのijのサイズを考えると、生成されるプロセスの総数を制限する必要があるでしょう。これは、parallelコマンドまたはxargsの一部のバージョンで実行できます。たとえば、-Pフラグをサポートするxargsを使用すると、次のように内部ループを並列化できます。

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

GNU parallel には、より高度な動作が必要な場合に備えて多数の機能があり、両方のパラメーターを簡単に並列化できます。

parallel -a <(seq 0800 9999) -a <(seq 001 032) -P 8 wget http://example.com/{1}-{2}.jpg
21
Steven D
for i in {1..3}; do
    for j in {10..20}; do
        (wget http://example.com/"$i-$j".jpg &)
    done
done

私もそれをテストしました...

3
confiq

これは非常に単純なアプローチです。この例では、スレッドを10に制限します。

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
3
steve

parallelが使用できない環境で同じ問題を解決したおおまかな方法​​は次のとおりです。これはbash機能に依存しているため、_#!/bin/bash_または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
_

_$MAX_CONCURRENT_を調整して、必要な(概算の)最大スレッド数を指定できます。そしてもちろん、_some_command_that_outputs_urls_と_do_something_with $url_を、シナリオにふさわしいものに置き換えます。たとえば、_some_command_that_outputs_urls \_という行を次のように置き換えます。

_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_something_with $url_

_wget $url
_

あなたに最終結果を与える

_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
_

これが機能する方法は、標準出力に(この場合は)URLのリストを生成し、その行を一度に1行ずつwhileループに読み込むコマンドです(改行に注意してください!)。 _$MAX_CONCURRENT_同時プロセスまで生成され、_$n_を使用して生成された数を追跡し、_$PIDS_を使用してプロセスIDを記録します。 _$MAX_CONCURRENT_プロセスが生成されると(実際に生成されるのは複合ステートメントであるため、その中に複数のコマンドとブロックを含めることができます)、生成されたPIDでwaitになります(これにより、指定されたPIDのいずれもまだ実行中でない場合は直ちに)、その内部状態をリセットしてから、別の実行を続行します。

再利用されたPIDの処理を改善するなど、このスクリプトを改善する方法はいくつかありますが、実行する必要がある環境で実行したかったので、私には十分です。私の実際のバージョンでは、タイムアウトも設定されており、cronを介して定期的に再実行されるため、この単純なバージョンと比較して、暴走の実行時間のリスクが大幅に減少します。

2
a CVn