如何运行不超过 n 个并行子 shell

如何运行不超过 n 个并行子 shell

我正在尝试从主脚本运行下标,但我想确保不超过n下标同时运行。

下面的简化示例进行了说明。

每个下标都会在 RAM ( ) 中创建一个虚拟文件,/dev/shm/其名称由唯一的时间戳组成,并在完成后将其删除。

主脚本计算源自下标的虚拟文件的数量/dev/shm/,并且如果其中 2 个或更多下标已在运行,则不会(不应该)启动新的下标。

然而,主脚本似乎忽略了 while 条件,并立即启动所有 5 个下标。

我的代码有什么问题吗?

主脚本.txt

#!/bin/bash
for counter in $(seq 1 5)
do
        while [ $(ls -1 /dev/shm/|grep "script044"|wc -l) -ge 2 ]
        do
                sleep 0
        done

        xterm -e "bash script044.txt" &
done

exit

script044.txt(下标)

#!/bin/bash

tempfilename="script044_"$(date +%Y%m%d%H%M%S)_${RANDOM}
echo > /dev/shm/${tempfilename}

for counter in $(seq 1 $(shuf -i 10-45 -n 1))
do
        sleep 1
        printf "${counter}\r"
done

rm /dev/shm/${tempfilename}

exit

答案1

(约定 -.txt只是纯文本文件。 .sh文件是 shell 脚本文件。)。

您的 mainscript.txt 脚本存在竞争条件。具体来说,while 循环在 script044.txt 脚本能够创建临时文件之前开始其下一次迭代。事实上,在创建任何这些文件之前,整个循环都会被迭代。

处理此类事情的更可靠的方法是忘记临时文件并使用内置的 shell wait

#!/bin/bash

pid_count=0
for counter in $(seq 1 5)
do
    xterm -e "bash script044.txt" &
    if (( ++pid_count > 2 )); then
        wait -n
        ((pid_count--))
    fi
done

每次启动子进程时都会增加计数器。如果计数器大于 3,则我们wait将完成下一个子流程。当wait返回时,我们递减计数器并再次开始下一个 xterm。

您可以从 script044.txt 中删除所有tempfilename与 - 相关的行 - 它们不再需要。


正如 @chepner 指出的,所需的-n选项仅在 bash 4.3 或更高版本中可用。

答案2

虽然如果您的目标是仅使用 shell 的解决方案,它不会有帮助,但 GNU 并行提供了一个sem命令可以帮助解决这种情况。

以下内容(未经测试,因为我没有你的脚本)应该运行你的作业 5 次,但一次只能运行 2 次,等待最后退出:

LIMIT=2
for i in {1..5}; do
    sem -j $LIMIT 'term -e "bash script044.txt"'
done
sem --wait

答案3

看起来你想要IPC。您可以等待子进程告知其何时完成,而不是每次都循环睡眠并进行测试。这就是管道的用途。

您可以让子进程向父进程报告。打开管道并与他们分享。当他们完成后,他们只需要让家长知道即可。

sub()(  trap "echo >&9" 0
        sleep 5
)
eval    "exec 9<>"<(echo);i=0
until   [ "$((i+=1))" -gt 5 ]
do      sub & read na <&9
        date +%S:%t"$i"
done

我使用进程替换打开它。如果您无法在 shell 中执行此操作,则可以使用:

mkfifo pipe; exec 9<>pipe; rm pipe; echo >&9

现在,在这两种情况下,初始echo都会将一根线放入管道中。这使您从一开始就提前一个等待进程 - 这意味着您将始终运行两个并发进程。该脚本用于date报告每次调用之间的秒数sub()。这是输出:

34: 1
39: 2
39: 3
44: 4
44: 5

那里。现在正如您所看到的,每 5 秒就有一个子进程死亡,当它死亡时,它就是一条通往当前阻塞的echo管道的线路。read一旦readfind 在输入中出现换行符,它就可以退出正在执行的操作并重新开始。

您只需trap在 script_044 中添加一个命令,指示子进程在完成时调用父亲。

答案4

在启动新实例后插入一些延迟xterm...

        xterm -e "bash script044.txt" &
        sleep 0.1

相关内容