如何在Linux中并行运行Shell脚本并收集进程的退出状态

时间:2020-02-23 14:40:23  来源:igfitidea点击:

并行运行shell脚本并收集Linux中各个后台进程的退出状态。

为什么要并行运行shell脚本?

计算能力不断提高不仅是因为处理器具有更高的时钟周期,而且还因为它们具有多个内核。

这意味着在单个硬件处理器中有多个逻辑处理器。
就像拥有多台计算机,而不只是一台计算机。

但是,除非软件使用多个内核,否则它们是无用的。
例如,一个执行大量计算的程序只能在一个内核上运行,而其他内核则处于空闲状态。
如果我们希望它更快,则该软件必须了解并利用多个内核。

让我们从示例脚本开始

在下面,我创建了三个子脚本和一个包装器脚本,我们将使用它们进行演示以并行运行Shell脚本,然后收集每个进程(脚本)的退出状态。

[root@node1 myscripts]# cat /tmp/myscripts/script1.sh
#!/bin/bash
sleep 5
exit 5
[root@node1 myscripts]# cat /tmp/myscripts/script2.sh
#!/bin/bash
sleep 10
exit 10
[root@node1 myscripts]# cat /tmp/myscripts/script3.sh
#!/bin/bash
sleep 12
exit 12

如我们所见,这些脚本只会休眠一段时间并抛出不同的退出代码,什么也不会做。
这里的主要思想是确保在不同时间间隔结束的脚本不会影响我们收集相应退出状态的功能。

首先,我有一个简单的脚本,它将按顺序调用这些脚本

# cat /tmp/run_sequential.sh
#!/bin/bash
tmp_file=$(mktemp /tmp/file.XXX)
for scripts in /tmp/myscripts/*; do
   sh $scripts
   exit_status=$?
   script_name=`echo $scripts | rev | awk -F "/" '{print }' | rev`
   echo "$script_name exit status: $exit_status"
done
rm -f $tmp_file

因此,这里我在/tmp/myscripts下调用各个脚本,然后稍后我将执行一些编辑(本教程不需要)以获取脚本名称以获得更好的输出。

让我们执行包装脚本并监视执行时间

# time /tmp/run_sequential.sh
script1.sh exit status: 5
script2.sh exit status: 10
script3.sh exit status: 12
real 0m27.044s
user 0m0.011s
sys 0m0.031s

因此,按照顺序方法的预期,该脚本花费了5 + 10 + 12秒=" 27秒"来执行。

现在让我们尝试并行方法。

要在bash中并行运行脚本,必须将单个脚本发送到后台。
因此,循环将不等待最后一个进程退出,而是将立即处理所有脚本。
接下来,我们可以放置一些功能来收集所有推送到后台的进程的退出状态,以便可以打印正确的退出状态。

# cat /tmp/run_parallel.sh
#!/bin/bash
tmp_file=$(mktemp /tmp/file.XXX)
for scripts in /tmp/myscripts/*; do
   sh $scripts &
   PID="$!"
   echo "$PID:$scripts" >> $tmp_file
   PID_LIST+="$PID "
done
for process in ${PID_LIST[@]};do
   wait $process
   exit_status=$?
   script_name=`egrep $process $tmp_file | awk -F ":" '{print }' | rev | awk -F "/" '{print }' | rev`
   echo "$script_name exit status: $exit_status"
done
rm -f $tmp_file

这如何运作?

我们利用Bash操作数&指示shell程序将命令发送到后台并继续执行脚本。
但是,这意味着我们的脚本将在循环完成后立即退出,而子脚本进程仍在后台运行。
为了防止这种情况,我们使用$!获取进程的PID,在Bash中它保存了最后一个后台进程的PID。
我们将这些PID添加到数组中,然后使用wait命令等待这些过程完成。

其中我们存储要发送到后台的每个进程的PID编号,并将其映射到脚本名称,以便以后可以将PID映射到相应的脚本。
一旦所有进程都发送到后台,我将使用"等待"功能等待PID_LIST中的PID退出,然后捕获并打印相应的退出状态。

现在让我们执行脚本并监视执行时间

# time /tmp/run_parallel.sh
myscripts exit status: 5
myscripts exit status: 10
myscripts exit status: 12
real 0m12.028s
user 0m0.030s
sys 0m0.044s

如我们现在所见,对于同一组脚本,包装程序仅花费了" 12秒"来执行。