从bash脚本启动进程失败

时间:2014-10-20 22:09:32

标签: linux bash

我有一个中央服务器,我定期启动一个检查远程服务器的脚本(来自cron)。检查是连续执行的,所以首先是一个服务器,然后是另一个服务器....

此脚本(来自中央服务器)在远程计算机上启动另一个脚本(让我们称之为update.sh),并且该脚本(在远程计算机上)正在执行以下操作:

processID=`pgrep "processName"` 
kill $processID
startProcess.sh

该进程被终止,然后在脚本startProcess.sh中启动如下:

pidof "processName"

if [ ! $? -eq 0 ]; then
    nohup "processName" "processArgs" >> "processLog" &
    pidof "processName"
    if [! $? -eq 0]; then
        echo "Error: failed to start process"
...

update.sh,startprocess.sh以及它启动的进程的实际二进制文件位于从中央服务器挂载的NFS上。

现在发生的事情有时,是我尝试在startprocess.sh中启动的进程未启动,我收到错误。奇怪的是,它是随机的,有时一台机器上的进程启动,同一台机器上的另一个时间无法启动。我正在检查大约300台服务器,错误总是随机的。

还有另一件事,远程服务器位于3个不同的地理位置(美国2个,欧洲1个),中央服务器位于欧洲。根据我的发现,到目前为止,美国的服务器比欧洲的服务器具有更多错误。

首先我认为错误必须与kill有关,所以我在kill和startprocess.sh之间添加了一个睡眠,但这没有任何区别。

似乎从startprocess.sh开始的进程根本没有启动,或者在启动它的时候发生了一些事情,因为日志文件中没有输出,应该是日志文件中的输出。

所以,我在这里寻求帮助

有没有人遇到过这种问题,或者知道可能出现什么问题?

感谢您的帮助

1 个答案:

答案 0 :(得分:4)

(对不起,但我原来的回答是错误的......这是更正)

使用$?获取startProcess.sh中后台进程的退出状态会导致错误的结果。男声明:

Special Parameters
?      Expands to the status of the most recently executed foreground
       pipeline.

正如您在评论中提到的,获取后台进程退出状态的正确方法是使用内置的wait。但为此必须处理SIGCHLD信号。< / p>

我为此制作了一个小测试环境,以展示它是如何工作的:

这是一个作为后台进程运行的脚本loop.sh

#!/bin/bash
[ "$1" == -x ] && exit 1;
cnt=${1:-500}
while ((++c<=cnt)); do echo "SLEEPING [$$]: $c/$cnt"; sleep 5; done

如果arg为-x,则退出状态为1以模拟错误。如果arg为num,则等待num * 5秒打印SLEEPING [<PID>] <counter>/<max_counter>到stdout。

第二个是启动器脚本。它在后台启动3个loop.sh脚本并打印其退出状态:

#!/bin/bash

handle_chld() {
    local tmp=()
    for i in ${!pids[@]}; do
        if [ ! -d /proc/${pids[i]} ]; then
            wait ${pids[i]}
            echo "Stopped ${pids[i]}; exit code: $?"
            unset pids[i]
        fi
    done
}

set -o monitor
trap "handle_chld" CHLD

# Start background processes
./loop.sh 3 &
pids+=($!)
./loop.sh 2 &
pids+=($!)
./loop.sh -x &
pids+=($!)

# Wait until all background processes are stopped
while [ ${#pids[@]} -gt 0 ]; do echo "WAITING FOR: ${pids[@]}"; sleep 2; done
echo STOPPED

handle_chld函数将处理SIGCHLD信号。设置选项monitor使非交互式脚本能够接收SIGCHLD。然后将陷阱设置为SIGCHLD信号。

然后开始后台进程。它们的所有PID都记在pids数组中。如果收到SIGCHLD,则在/ proc /目录中检查哪个子进程已停止(缺少的进程)(也可以使用kill -0 <PID> 内置进行检查。等待后,后台进程的退出状态存储在着名的$?伪变量中。

主脚本等待所有pid停止(否则它无法获得其子项的退出状态)并且它自行停止。

示例输出:

WAITING FOR: 13102 13103 13104
SLEEPING [13103]: 1/2
SLEEPING [13102]: 1/3
Stopped 13104; exit code: 1
WAITING FOR: 13102 13103
WAITING FOR: 13102 13103
SLEEPING [13103]: 2/2
SLEEPING [13102]: 2/3
WAITING FOR: 13102 13103
WAITING FOR: 13102 13103
SLEEPING [13102]: 3/3
Stopped 13103; exit code: 0
WAITING FOR: 13102
WAITING FOR: 13102
WAITING FOR: 13102
Stopped 13102; exit code: 0
STOPPED

可以看出退出代码是正确报告的。

我希望这会有所帮助!