我正在使用以下逻辑从阵列中立即下载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[@]}
答案 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
,以确保构建的命令完全符合您的要求