运行 3 个并行下载并随时取消所有下载

运行 3 个并行下载并随时取消所有下载

我需要一个可以执行以下操作的 bash 脚本:

  1. 使用 wget 并行下载 3 个文件,它们不必同时完成。
  2. 可以同时取消 3 个下载CTRL+C(已经完成的下载除外)。

目前,我wget -c URL在各自的终端上运行 3 个并行实例。但只想有一个等待用户输入取消的脚本,这样我就可以在 Linux 重新启动后继续。

我的系统是带有 Cygwin 的 Windows。

答案1

您可以使用 GNUparallel来实现这一点。将 URL 放入数组中,然后wget运行parallel

#!/bin/bash
urls=( "https://example.com" "https://example1.com" "https://example2.com")

parallel wget -c ::: "${urls[@]}"

Parallel 将并行运行命令,并停止脚本(例如使用Ctrl+C将终止并行,终止所有三个正在运行的 wget 实例,同时不影响任何已完成的实例。

您应该能够parallel在操作系统的存储库中找到。


如果您不想或无法安装parallel,您也可以在 bash 中本地执行此操作:

#!/bin/bash

trap 'kill $(jobs -p)' EXIT

urls=( "https://example.com" "https://example1.com" "https://example2.com" )
for url in "${urls[@]}"; do
  wget -c "$url" &
done

wait

这里的技巧是使用trap内置函数(参见help trapbash)来陷阱kill $(jobs -p)EXIT 信号的命令。jobs -p将返回当前 shell 的所有子进程的 PID,因此在本例中它将是正在运行的wget进程的 PID。这些被传递到kill杀死它们的地方。因此,当您终止脚本时,它会传递给其子脚本。确保wait脚本将等到所有后台作业完成运行后再退出,以便您仍然可以wget使用Ctrl+终止脚本,进而终止进程C

答案2

Bash 脚本位于这个另一个答案需要陷阱,因为 Bash 在单独的进程组中运行命令(包括异步命令)。终端知道前台进程组并在Ctrl+上向该组发送 SIGINT c。当 Bash 脚本wait位于前台时,它bash本身位于前台进程组中,但不是wget它所生成的。 SIGINT(如果有)到达bash,而不是wgets。这就是为什么您需要上述答案中的陷阱。

其他 shell 的工作方式可能有所不同。在我的 Kubuntu 中,posh不会创建新的进程组,它会运行它所属的进程组中的所有内容。这一事实使我们能够简化脚本:

#!/usr/bin/posh

set -- 'https://example.com' 'https://example1.com' 'https://example2.com'
for url do
  wget -c "$url" &
done

wait

如果当我们的脚本在前台运行时按Ctrl+ ,终端将直接发送 SIGINT 到cposh wgets 因为它们将位于与前台进程组相同的进程组中。

笔记:

  • posh不支持数组,所以我将 URL 保存为位置参数。您可以删除该set …行;然后在调用脚本时指定 URL(例如./the_script 'https://example.com' 'https://example1.com' 'https://example2.com')。通过这种方式,您可以将脚本变成并行下载任意内容的工具。
  • 另一个答案kill在陷阱中使用,默认信号是 SIGTERM,它与 SIGINT 不同(尽管wget可能以相同的方式做出反应)。您可以发送killSIGINT 或其他任何信号,这是您的选择(kill -s …)。在我们的脚本中我们无法选择。我们不传递任何信号,我们不生成任何信号。我们首先设置好一切,因此在Ctrl+c时,终端不仅向解释脚本的 shell 发送 SIGINT,还向wgets 发送。

另一个答案很好,GNUparallel是正确的工具,Bash 应该trap可以工作;所以从某种意义上说我的回答是不必要的。我仍然想向您展示,了解Ctrl+ 的c工作原理以及不同 shell 的工作原理,您只需选择不同的 shell 即可获得所需的行为。

相关内容