我看了又看,找不到一个 bash 脚本的工作解决方案,我正在尝试创建该脚本来关闭进程并等待它并生成进程完成。我还在学习很多 Linux 知识。
语境:进程 FOO 运行。进程BAR用于检查FOO,也用于杀死它(我无法控制这两个进程的内部)。我所能做的就是将命令传递给 BAR,然后它执行它们。
在本例中,我向 BAR 发送命令来终止 FOO,它会生成另一个进程并将其置于后台。
目标:我试图同时运行 20 个命令来对 20 个 FOO 执行终止(通过 BAR),并等待所有 FOO 死亡,然后再继续执行脚本的下一部分。
问题:到目前为止,我所能做的就是等待 BAR 执行,并在后台进程杀死 FOO 之前继续执行。
BAR exit FOO1
BAR exit FOO2
...
BAR exit FOO20
wait
do more stuff
我也尝试过
BAR exit FOO1
PID1=$!
BAR exit FOO2
PID2=$!
wait $PID1 $PID2
没有运气。
答案1
如果您没有正在等待的其他进程,wait
则不带参数的 a 将等待所有后台作业完成。我经常使用类似的结构
for i in *; do md5sum $i & done
wait
但是,如果您的脚本有任何其他后台任务,这将会中断,但您可以将其包装在子 shell 中,例如
(for i in *; do md5sum $i & done; wait) & all_md5sum=$!
...
wait $all_md5sum
答案2
首先尝试一个更简单的场景,例如只有 2 个进程FOO1
和FOO2
,并且如果您要在名为 的脚本中运行parent.sh
:
#!/bin/bash
FOO1 &
pid1=$!
echo "Child PID=$pid1 launched"
FOO2 &
pid2=$!
echo "Child PID=$pid2 launched"
BAR exit FOO1
BAR exit FOO2
echo "Pausing to wait for children to finish..."
wait $pid1
wait $pid2
echo "Children finished, moving on."
看看这是否适用于 2 个FOO
s,如果适用,则适用于 20 个。
解释
由于我们无法看到FOO
s 和BAR
流程的内部结构,因此我仅依赖您发布的内容,据我了解,您说
- “进程BAR用于检查FOO,也用于杀死它”
- 意思是,在您发布片段之前的某个时刻,您以
FOO1
某种方式开始,以便BAR exit FOO1
影响它
在您最初发布的片段中,您还写道:
BAR exit FOO1
PID1=$!
$!
捕获最近的后台进程- 但
BAR exit FOO1
不是一个进入后台的进程,它正如你所说,是使用 BAR 控制 FOO 的一种手段:“BAR 用于检查 FOO,也用于杀死它” - 因此
$!
不太可能捕获FOO1
pid
因此,FOO
请确保在实际启动 * 进程的地方捕获 pid。例如,如果您全部运行在 中parent.sh
,并且只有 2 个进程,您将执行以下操作:
#!/bin/sh
FOO1 &
pid1=$!
echo "Child PID=$pid1 launched"
FOO2 &
pid2=$!
echo "Child PID=$pid2 launched"
- 小写
pid1
,你也可以使用原来的大写,只要一致,我只是更喜欢使用小写作为约定,以区别被误认为是环境变量 #
echo 是可选跟踪,因此我们知道发生了什么,在排除故障时使用它,并在解决此问题后将其注释掉或删除它
然后我们执行您声称用于检查和终止的命令FOO1
:FOO2
BAR exit FOO1
BAR exit FOO2
然后,
echo "Pausing to wait for children to finish..."
wait $pid1
wait $pid2
echo "Children finished, moving on."
- 同样,
echo
s 是可选的,但在您仍在排除故障时提供信息,让我们知道发生了什么 wait
对于每个人$pid
...- 接下来是另一个选项
echo
,让您知道wait
ing 已结束
信用
射击 2009免费 pdf 第 506 页上的异步脚本示例
答案3
您可以为每个命令触摸一个文件并检查该文件。这是一个简单的例子:
#!/usr/local/bin/bash
# Flag to knkow when completed
finished=0;
# The commands t run
declare -a commands=('cmd1' 'cmd2' 'cmd3' 'cmd4');
# Fork all commands
for cmd in "${commands[@]}"; do
./$cmd &
done
# Loop to wait for all processes to finish
loop=0;
while [ $finished -eq 0 ]; do
# Just for visual effect
loop=$((loop+1));
echo "Loop $loop";
sleep 1;
files=0;
for cmd in "${commands[@]}"; do
# Each program shoudl touch a file so that we
# can know it has completed successfully
if [ -e ".$cmd" ]; then
echo "Process $cmd completed";
files=$((files+1));
else
# No need to continue if anything is not done
# Error checking here like a TTL value or ps -ef | grep pid etc
echo "Waiting for process $cmd";
break;
fi;
done
# if we have as many files as executed commands
if [ "$files" -eq ${#commands[@]} ]; then
finished=1;
fi
done;
echo 'All jobs completed successfully';
# Clean up after yourself
for cmd in "${commands[@]}"; do
rm -f ".$cmd";
done;
然后,为了测试每个 cmd1、cmd2 ... cmdN 可以具有如下内容:
#!/usr/local/bin/bash
num=1;
echo "Enter cmd$num";
sleep 1; # use different sleep values to simulate execution
touch ".cmd$num"
echo "Exit cmd$num";
答案4
如果 $PID1 正在运行,这将评估为 true: if [kill -s 0 $PID1 ]
您可以有多个 if 子句。如果 PID 未运行,它们将评估为 false。该子句(与其他子句组合)应根据您的需要控制脚本的流程。