在bash脚本中并行下载文件

时间:2018-03-15 09:51:57

标签: bash shell unix

我正在使用以下逻辑从阵列中立即下载3个文件,一旦完成所有3个文件,将只接收下3个文件。

parallel=3
       downLoad() {
                while (( "$#" )); do
                    for (( i=0; i<$parallel; i++ )); do
                        echo "downloading ${1}..."
                        curl -s -o ${filename}.tar.gz <download_URL> &
                        shift
                    done
                    wait
                                echo "#################################"
            done
        }

       downLoad ${layers[@]}

但我期待的是&#34;在任何时候都应该运行3次下载&#34; - 我的意思是假设我们向后台发送了3个文件下载,并且很快就完成了3个文件中的一个因为尺寸非常小,我希望队列中的另一个文件应该发送下载。

完整的脚本:

#!/bin/bash

set -eu

reg="registry.hub.docker.com"
repo="hjd48"
image="redhat"
name="${repo}/${image}"
tag="latest"
parallel=3

# Get auth token
token=$( curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${name}:pull" | jq -r .token )

# Get layers
resp=$(curl -s -H "Authorization: Bearer $token" "https://${reg}/v2/${name}/manifests/${tag}" | jq -r .fsLayers[].blobSum )
layers=( $( echo $resp | tr ' ' '\n' | sort -u ) )

prun() {
    PIDS=()
    while (( "$#" )); do
        if ( kill -0 ${PIDS[@]} 2>/dev/null ; [[ $(( ${#PIDS[@]} - $? )) -lt $parallel ]])
        then
                echo "Download: ${1}.tar.gz"
                curl -s -o $1.tar.gz -L -H "Authorization: Bearer $token" "https://${reg}/v2/${name}/blobs/${1}" &
                PIDS+=($!)
                shift
        fi
    done
    wait
}

prun ${layers[@]}

4 个答案:

答案 0 :(得分:2)

如果您不介意使用xargs,那么您可以:

xargs -I xxx -P 3 sleep xxx < sleep

睡觉是:

1
2
3
4
5
6
7
8
9

如果你用以下方式观看背景:

watch -n 1 -exec ps  --forest -g -p your-Bash-pid

睡眠可能是您的链接数组)然后您将看到 3 作业并行运行,当这三个作业中的一个完成后,将添加下一个作业。事实上, 3 作业一直在运行,直到阵列结束。

watch(1)的示例输出:

12260 pts/3    S+     0:00  \_ xargs -I xxx -P 3 sleep xxx
12263 pts/3    S+     0:00      \_ sleep 1
12265 pts/3    S+     0:00      \_ sleep 2
12267 pts/3    S+     0:00      \_ sleep 3

xargs 3 作业开始,当其中一个作业完成时,它会添加下一个bacomes:

12260 pts/3    S+     0:00  \_ xargs -I xxx -P 3 sleep xxx
12265 pts/3    S+     0:00      \_ sleep 2
12267 pts/3    S+     0:00      \_ sleep 3
12269 pts/3    S+     0:00      \_ sleep 4 # this one was added

答案 1 :(得分:1)

我已经通过使用trap来处理SIGCHLD并在结束时开始另一次转移来完成此操作。

困难的部分是,一旦您的脚本使用SIGCHLD行安装trap处理程序,您就无法创建除传输过程之外的任何子进程。例如,如果您的shell没有内置echo,则调用echo会生成一个子进程,导致您在echo进程时再启动一次传输结束。

我没有副本,但它是这样的:

startDownload() {
   # only start another download if there are URLs left in
   # in the array that haven't been downloaded yet
   if [ ${ urls[ $fileno ] } ];
       # start a curl download in the background and increment fileno
       # so the next call downloads the next URL in the array
       curl ... ${ urls[ $fileno ] } &
       fileno=$((fileno+1))
   fi
}

trap startDownload SIGCHLD

# start at file zero and set up an array
# of URLs to download
fileno=0
urls=...

parallel=3

# start the initial parallel downloads
# when one ends, the SIGCHLD will cause
# another one to be started if there are
# remaining URLs in the array
for (( i=0; i<$parallel; i++ )); do
    startDownload
done

wait

根本没有经过测试,可能存在各种错误。

答案 2 :(得分:0)

我会将所有提供的文件名读入三个变量,然后分别处理每个流,例如

PARALLEL=3
COUNTER=1
for FILENAME in $#
do
        eval FILESTREAM${COUNTER}="\$FILESTREAM${COUNTER} \${FILENAME}"
        COUNTER=`expr ${COUNTER} + 1`

        if [ ${COUNTER} -gt ${PARALLEL} ]
        then
                COUNTER=1
        fi
done

现在并行调用每个流的下载功能:

COUNTER=1

while [ ${COUNTER} -le ${PARALLEL} ]
do
        eval "download \$FILESTREAM${COUNTER} &"
        COUNTER=`expr ${COUNTER} + 1`
done

答案 3 :(得分:0)

除了从头开始实现并行bash脚本之外,GNU parallel是一个可用的工具,非常适合执行这些类型的任务。

parallel -j3 curl -s -o {}.tar.gz download_url ::: "${layers[@]}"
  • -j3确保最多同时运行3个作业
  • 您可以在--dry-run之后添加其他选项parallel,以确保构建的命令完全符合您的要求
相关问题