在 macOS 上等待任何子进程退出?

Waiting ANY child process exit on macOS?

我想知道如何在 macOS 中等待任何进程完成,因为 wait -n 不起作用。我有一个脚本做几件事,在某些时候它会进入一个循环,调用另一个脚本到后台以利用一些并行性,但不会超过 X 次,因为它效率不高。因此,在创建新进程之前,我需要等待任何子进程完成。

我看过 this question 但它没有回答“任何”部分,它只是说如何等待特定进程完成。

我曾考虑过存储所有 PID 并主动检查它们是否仍然 运行 ps,但这是非常草率和耗费资源的。我还考虑过将 bash 升级到更新的版本(如果这在 macOS 中是可能的而不破坏 bash 已经工作的方式),但如果没有其他方法可以实际等待 要完成的任何 过程,这是一个非常基本的功能...有什么想法吗?

我的代码的基本版本如下所示:

for vid_file in $VID_FILES
do
    my_script.sh $vid_file other_args &
    ((TOTAL_PROCESSES=TOTAL_PROCESSES+1))
    if [ $TOTAL_PROCESSES -ge $MAX_PROCESS ]; then
        wait -n
        ((TOTAL_PROCESSES=TOTAL_PROCESSES-1))
    fi
done

我用既不优雅也不高效的方法来替代 wait -n:

NUM_PROCC=$MAX_PROCESS
while [ $NUM_PROCC -ge $MAX_PROCESS ]
do
    sleep 5
    NUM_PROCC=$(ps | grep "my_script.sh"| wc -l | tr -d " \t")
    # grep command will count as one so we need to remove it
    ((NUM_PROCC=NUM_PROCC-1))
done

PS:这个问题可以关闭并与我上面提到的问题合并。我刚刚创建了这个新的,因为 Whosebug 不允许我发表评论或提问...

PS2:我明白我的 objective 可以通过其他方式实现。如果您对特定问题本身没有答案,而是有解决方法,请让其他人回答有关“等待任何”的问题,因为它在将来对 me/everyone 也非常有用。我当然会欢迎并感谢解决方法!

提前致谢!

您似乎只想限制同时处于 运行 的进程数。这是使用 bash <= 4.2:

的基本方法
#!/bin/bash

MAX_PROCESS=2
INPUT_PATH=/somewhere

for vid_file in "$INPUT_PATH"/*
do   
    while [[ "$(jobs -pr | wc -l)" -ge "$MAX_PROCESS" ]]; do sleep 1; done
    my_script.sh "$vid_file" other_args &     
done
wait

这是 bash >= 4.3 版本:

#!/bin/bash

MAX_PROCESS=2
INPUT_PATH=/somewhere

for vid_file in "$INPUT_PATH"/*
do   
    [[ "$(jobs -pr | wc -l)" -ge "$MAX_PROCESS" ]] && wait -n
    my_script.sh "$vid_file" other_args &     
done
wait

建议 1:完成指标文件

建议将任务完成指示文件添加到您的 my_script.sh

像这样:

 echo "[=10=].$(date +%F_%T).done" >> my_script.sh

并在您的部署脚本中测试完成指示器文件是否存在。

rm "my_script.sh.*.done"
my_script.sh "$vid_file" other_args &     
while [[ ! -e "my_script.sh.*.done" ]]; do
    sleep 5
done

不要忘记清理完成指示器文件。

这种方法的优点:

  1. 简单
  2. 所有 shell 都支持
  3. 完成后保留 history/audit 踪迹

这种方法的缺点:

  1. 需要修改原始脚本my_script.sh
  2. 需要清理。
  3. 使用循环

建议 2:使用 wait 命令和 pgrep 命令

建议了解有关 wait 命令的更多信息 here

建议了解有关 pgrep 命令的更多信息 here

my_script.sh "$vid_file" other_args &     
wait $(pgrep -f "my_script.sh $vid_file")

这种方法的优点:

  1. 简单
  2. 可读

这种方法的缺点:

  1. 多个用户同时使用相同的命令
  2. wait 命令特定于 Linux bash 可能在其他 shell 中也是如此。检查您当前的支持。

GNU make 具有并行化功能,下面的 Makefile 应该可以与 macOS 附带的非常老的 make 3.81 一起使用。用制表符替换 my_script.sh 之前的 4 个前导空格,并将其存储在名为 Makefile:

的文件中
.PHONY: all $(VID_FILES)
all: $(VID_FILES)

$(VID_FILES):
    my_script.sh "$@" other_args

然后 运行 并行最多 8 个作业:

$ make -j8 VID_FILES="$VID_FILES"

Make 可以做得更好:避免重做已经完成的事情:

TARGETS := $(patsubst %,.%.done,$(VID_FILES))
.PHONY: all clean
all: $(TARGETS)

$(TARGETS): .%.done: %
    my_script.sh "$<" other_args
    touch "$@"

clean:
    rm -f $(TARGETS)

在最后一个版本中,为每个处理过的视频 foo 创建了一个空标签文件 .foo.done。如果以后你re-run制作和视频foo没变,就不会re-processed了。键入 make clean 以删除所有标记文件。不要忘记用制表符替换前导空格。

对于 GNU Parallel,它看起来像:

parallel my_script.sh {} other_args ::: $VID_FILES