杀死在while循环后台进程中创建的PID

时间:2017-04-17 23:50:20

标签: bash

所以我有一个while循环,循环遍历包含名称的文本文件,然后在列表上执行后台进程。这一切都很好,但是当它结束时,我需要ctrl-c。完成所有过程后如何执行SIGINT,所以我不需要ctrl-c?

我是否需要在每个流程中执行此操作?或者我可以通过使用像睡眠这样的东西在循环中做到这一点吗?

这是我目前所拥有的,但不太有用。

while read BAR; do foo $BAR & done < bars.txt; pid=$!; wait; kill pid

3 个答案:

答案 0 :(得分:1)

现在您只收集最后 PID,而不是所有后台进程的PID。

如果您收集PID,您可以单独等待它们 - 这可以让您检查各个作业是否失败,以查看它们中的哪一个未实际完成:< / p>

pids=( )
while read bar; do
  foo "$bar" & pids+=( "$!" )
done < bars.txt

for pid in "${pids[@]}"; do
  echo "Checking exit status of $pid..."
  if wait "$pid"; then
    echo "$pid succeeded!"
  else
    echo "$pid failed!"
  fi
done

如果您想要追踪哪些特定处理成功或失败,您可以做得更好(使用bash 4.0或更高版本):

declare -A pids=( )
while read -r bar; do
  foo "$bar" & pids[$!]="$bar"
done < bars.txt

for pid in "${!pids[@]}"; do
  bar=${pids[$pid]}
  echo "Checking exit status of $pid (processing value $bar)..."
  if wait "$pid"; then
    echo "$pid (for $bar) succeeded!"
  else
    echo "$pid (for $bar) failed!"
  fi
done

在上述任何一种情况下,wait的每次调用都负责在相应的PID退出后返回。因此,当所有后台任务都退出时,您的脚本将自行退出。

最后,要强制您的孩子退出,您可以构建一个利用PID列表的信号处理程序:

shutdown() { kill "${pids[@]}"; } # or "${!pids[@]}" for the second example
trap shutdown 0

答案 1 :(得分:1)

使用GNU Parallel,它看起来像这样:

parallel -j0 foo {} < bars.txt
echo $? jobs failed

如果省略-j0,它将为每个CPU核心运行一个作业。

如果您需要确切知道哪些作业失败,您可以使用作业日志:

parallel --joblog jl -j0 foo {} < bars.txt
cat jl

它还确保不同作业的输出不会混合在一起,因此如果您使用输出,则可以保证您不会从两个不同的作业中获得半个行。

GNU Parallel是一个通用的并行程序,可以很容易地在同一台机器上或在你有ssh访问权限的多台机器上并行运行作业。

如果要在4个CPU上运行32个不同的作业,并行化的直接方法是在每个CPU上运行8个作业:

Simple scheduling

GNU Parallel会在完成后生成一个新进程 - 保持CPU处于活动状态,从而节省时间:

GNU Parallel scheduling

<强>安装

您应该使用软件包管理器安装GNU Parallel,但如果没有为您的发行版打包GNU Parallel,则可以进行个人安装,不需要root访问权限。这可以在10秒内完成:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

有关其他安装选项,请参阅http://git.savannah.gnu.org/cgit/parallel.git/tree/README

了解详情

查看更多示例:http://www.gnu.org/software/parallel/man.html

观看介绍视频:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

完成教程:http://www.gnu.org/software/parallel/parallel_tutorial.html

注册电子邮件列表以获得支持:https://lists.gnu.org/mailman/listinfo/parallel

答案 2 :(得分:0)

如果你真的想要一个单行,你可以围绕以下逻辑构建它(为了更好的可读性而格式化)。

while -r read BAR
do
  foo "$BAR" &
done < bars.txt
wait

这将等待所有后台任务完成(包括执行此命令之前启动的任何后台任务)。这有点粗糙(CharlesDuffy已经提出了一个允许更好地跟踪任务的解决方案),但它具有足够短的单线程的可疑优势。

while -r read BAR ; do foo "$BAR" & done < bars.txt ; wait

要在启动它们之后立即杀死它们(我不确定你为什么要这样做),你可以这样做:

while -r read BAR ; do foo "$BAR" & done < bars.txt ; kill $(jobs -p)

再一次,这将发出所有后台工作的信号,而不仅仅是您刚刚推出的工作。