我正在尝试从主脚本运行下标,但我想确保不超过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
一旦read
find 在输入中出现换行符,它就可以退出正在执行的操作并重新开始。
您只需trap
在 script_044 中添加一个命令,指示子进程在完成时调用父亲。
答案4
在启动新实例后插入一些延迟xterm
...
xterm -e "bash script044.txt" &
sleep 0.1