【问题标题】:Check running status of a background process launched from same bash script检查从同一 bash 脚本启动的后台进程的运行状态
【发布时间】:2016-02-06 11:59:38
【问题描述】:

我必须编写一个 bash 脚本,根据传递的命令行参数在 background 中启动一个进程,如果它能够成功 run 启动 返回程序。

这是我想要实现的伪代码

if [ "$1" = "PROG_1" ] ; then
    ./launchProg1 &
    if [ isLaunchSuccess ] ; then
        echo "Success"
    else
        echo "failed"
        exit 1
    fi
elif [ "$1" = "PROG_2" ] ; then
    ./launchProg2 &
    if [ isLaunchSuccess ] ; then
        echo "Success"
    else
        echo "failed"
        exit 1
    fi
fi

脚本不能waitsleep 因为它将被另一个关键任务c++ 程序调用并且需要高吞吐量(每秒启动的进程数),而且进程的运行时间是未知的。脚本既不需要捕获任何输入/输出,也不需要等待启动的进程完成。

我没有成功尝试以下方法:

#Method 1
if [ "$1" = "KP1" ] ; then
    echo "The Arguement is KP1"
    ./kp 'this is text' &
    if [ $? = "0" ] ; then
        echo "Success"
    else
        echo "failed"
        exit 1
    fi
elif [ "$1" = "KP2" ] ; then
    echo "The Arguement is KP2"
    ./NoSuchCommand 'this is text' &
    if [ $? = "0" ] ; then
        echo "Success"
    else
        echo "failed"
        exit 1
    fi
#Method 2
elif [ "$1" = "CD5" ] ; then
    echo "The Arguement is CD5"
    cd "doesNotExist" &
    PROC_ID=$!
    echo "PID is $PROC_ID"
    if kill -0 "$PROC_ID" ; then
        echo "Success"
    else
        echo "failed"
        exit 1
    fi
#Method 3
elif [ "$1" = "CD6" ] ; then
    echo "The Arguement is CD6"
    cd .. &
    PROC_ID=$!
    echo "PID is $PROC_ID"
    ps -eo pid | grep "$PROC_ID" && { echo "Success"; exit 0; }
    ps -eo pid | grep  "$PROC_ID" || { echo "failed" ; exit 1; }
else
    echo "Unknown Argument"
    exit 1
fi

运行脚本会产生不可靠的输出。方法 1、2 始终返回 Success,而方法 3 在进程执行在检查之前完成时返回 failed

这是在GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) 上测试的样本

[scripts]$ ./processStarted3.sh KP1
The Arguement is KP1
Success
[scripts]$ ./processStarted3.sh KP2
The Arguement is KP2
Success
./processStarted3.sh: line 13: ./NoSuchCommand: No such file or directory
[scripts]$ ./processStarted3.sh CD6
The Arguement is CD6
PID is 25050
failed

正如在类似问题中所建议的,我不能使用进程名称,因为不能应用 one process may be executed several timesothers

我没有尝试过screentmux,因为获得在生产服务器上安装它们的权限并不容易(但如果这是唯一的选择,我会这样做)

更新
@ghoti
./kp 是存在的程序,启动程序返回Success./NoSuchCommand 不存在。从 (edited) 输出中仍然可以看到,脚本错误地返回 Success

进程何时完成执行或程序异常终止无关紧要。通过脚本启动的程序不会以任何方式被跟踪(因此我们不会将pid 存储在任何表中,也没有必要使用deamontools)。

@Etan Reisner
无法启动的程序示例是./NoSuchCommand,它不存在。或者可能是一个无法启动的损坏程序。

@Vorsprung
调用在后台启动程序的脚本不会花费很多时间(并且可以根据我们的期望进行管理)。但是sleep 1 会随着时间的推移累积而导致问题。

上述#Method3 工作正常,除非进程在ps -eo pid | grep "$PROC_ID" && { echo "Success"; exit 0; } 检查可以执行之前终止。

【问题讨论】:

  • 您是否需要测试它们是否仍在运行或是否完​​全正确运行?如果它们仍在运行,您如何判断它们是否正确运行?以后的失败不重要吗?如果他们开始运行,您是否关心他们是否在某个时候失败?您需要报告哪些具体标准?
  • 如果进程启动成功与否,脚本将返回。无需检查它们是否仍在运行或稍后异常终止。
  • 这些程序在什么情况下无法启动?如果以后失败无关紧要,这是一个现实的担忧吗?
  • @parthasarathy - 请update your question 提供这些详细信息。评论是 cmets 的好地方。
  • 另外,./kp./NoSuchCommand 真的存在吗?或者这就是你真正想要测试的?

标签: linux bash shell


【解决方案1】:

这里是一个例子,它会显示一个进程是否成功启动的结果。

#!/bin/bash
$1 & #executes a program in background which is provided as an argument
pid=$! #stores executed process id in pid
count=$(ps -A| grep $pid |wc -l) #check whether process is still running
if [[ $count -eq 0 ]] #if process is already terminated, then there can be two cases, the process executed and stop successfully or it is terminated abnormally
then
        if wait $pid; then #checks if process executed successfully or not
                echo "success"
        else                    #process terminated abnormally
                echo "failed (returned $?)"
        fi
else
        echo "success"  #process is still running
fi

#Note: The above script will only provide a result whether process started successfully or not. If porcess starts successfully and later it terminates abnormally then this sciptwill not provide a correct result

【讨论】:

  • 是的,这可以正常工作,无需等待任何已启动的进程
【解决方案2】:

接受的答案不像宣传的那样有效。

此检查中的计数将始终至少为 1,因为“grep $pid”将同时找到带有 $pid 的进程(如果存在)和 grep。

count=$(ps -A| grep $pid |wc -l)
if [[ $count -eq 0 ]]
then
    ### We can never get here
else
    echo "success"  #process is still running
fi

更改上述内容以检查计数是否为 1 或从计数中排除 grep 应该使原始工作正常。

这是原始示例的替代(可能更简单)实现。

#!/bin/bash
$1 & # executes a program in background which is provided as an argument
pid=$! # stores executed process id in pid

# check whether process is still running
# The "[^[]" excludes the grep from finding itself in the ps output
if ps | grep "$pid[^[]" >/dev/null
then
    echo "success (running)"  # process is still running
else
    # If the process is already terminated, then there are 2 cases:
    # 1) the process executed and stop successfully
    # 2) it is terminated abnormally

    if wait $pid # check if process executed successfully or not
    then
        echo "success (ran)"
    else
        echo "failed (returned $?)" # process terminated abnormally
    fi
fi

# Note: The above script will detect if a process started successfully or not. If process is running when we check, but later it terminates abnormally then this script will not detect this.

【讨论】:

  • ps -A| grep 121233121233 是不正确的pid)永远不会返回任何内容,因为ps -A 的输出是馈送到grep。我已经在 rhel 6.6Ubuntu 16.04 上测试过这个
【解决方案3】:

使用jobs

为了演示,将以下内容放入 bash 脚本并执行

#!/bin/bash

echo === still running ===================
{ sleep 1 ; echo done ; } &
sleep 0.1
jobs
wait

echo === done with zero exit status ======
echo done &
sleep 0.1
jobs
wait

echo === done with nonzero exit status ===
false &
sleep 0.1
jobs
wait

echo === command not found ===============
notexisting &
sleep 0.1
jobs
wait

echo === not executable ==================
./existingbutnotexecutable &
sleep 0.1
jobs
wait

输出

$ ./jobcontrol.sh 
=== still running ===================
[1]+  Running                 { sleep 1; echo done; } &
done
=== done with zero exit status ======
done
[1]+  Done                    echo done
=== done with nonzero exit status ===
[1]+  Exit 1                  false
=== command not found ===============
jobcontrol.sh: line 26: notexisting: command not found
[1]+  Exit 127                notexisting
=== not executable ==================
jobcontrol.sh: line 33: ./existingbutnotexecutable: Permission denied
[1]+  Exit 126                ./existingbutnotexecutable

(文件existingbutnotexecutable必须存在且不可执行)

jobs 的输出我们可以区分:

  • 仍在运行的后台作业
  • 已完成运行的作业
  • 以非零退出状态运行的作业
  • 由于找不到命令而无法运行的作业
  • 以及由于不可执行而无法运行的作业。

也许还有更多案例,但我没有研究更多。

wait 是为了确保一次不超过一个后台作业。这仅用于测试和演示目的。对于生产版本,您可以省略 wait

另一方面,sleep 0.1 是为了防止竞争条件。 jobs 似乎非常快,甚至在后台作业正确启动之前就会开始和完成并报告结果。没有sleepjobs 命令似乎总是说“正在运行”,并且总是在后台命令的结果之前完成。错误与否。

也许有其他方法可以防止没有sleep 的比赛。我没有深入研究。在我的测试中,sleep 0 仍然会失败(竞争条件)大约 10 次。也许sleep 0.01 足够可靠和足够快。


这里是一个基于jobs输出的人性化输出示例

#!/bin/bash

isrunsuccess() {
  sleep 0.1
  case $(jobs) in
    *Running*)   echo "status: running" ;;
    *Done*)      echo "status: done" ;;
    *Exit\ 127*) echo "status: not found" ;;
    *Exit\ 126*) echo "status: not executable" ;;
    *Exit*)      echo "status: done nonzero exitstatus" ;;
  esac
}

echo === still running ===================
{ sleep 1 ; echo done ; } &
isrunsuccess
wait

echo === done with zero exit status ======
echo done &
isrunsuccess
wait

echo === done with nonzero exit status ===
false &
isrunsuccess
wait

echo === command not found ===============
notexisting &
isrunsuccess
wait

echo === not executable ==================
./existingbutnotexecutable &
isrunsuccess
wait

输出

$ ./jobcontrol.sh 
=== still running ===================
status: running
done
=== done with zero exit status ======
done
status: done
=== done with nonzero exit status ===
status: done nonzero exitstatus
=== command not found ===============
./jobcontrol.sh: line 41: notexisting: command not found
status: not found
=== not executable ==================
./jobcontrol.sh: line 47: ./existingbutnotexecutable: Permission denied
status: not executable

您可以合并“已运行”和“未运行”案例

isrunsuccess() {
  sleep 0.1
  case $(jobs) in
    *Exit\ 127*|*Exit\ 126*) echo "status: did not run" ;;
    *Running*|*Done*|*Exit*) echo "status: did run or still running" ;;
  esac
}

输出

$ ./jobcontrol.sh 
=== still running ===================
status: did run or still running
done
=== done with zero exit status ======
done
status: did run or still running
=== done with nonzero exit status ===
status: did run or still running
=== command not found ===============
./jobcontrol.sh: line 50: notexisting: command not found
status: did not run
=== not executable ==================
./jobcontrol.sh: line 56: ./existingbutnotexecutable: Permission denied
status: did not run

在 bash 中检查字符串内容的其他方法:How do you tell if a string contains another string in POSIX sh?

bash 文档说明 exitstatus 127 表示未找到,126 表示不可执行:https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html

【讨论】:

    【解决方案4】:

    很抱歉错过了“脚本不能等待或休眠”这一要求

    启动后台程序,获取它的 pid。等一下。然后用 kill -0 检查它是否仍在运行

    kill -0 状态取自 $?这用于确定进程是否仍在运行

    #!/bin/bash
    
    ./$1 &
    pid=$!
    
    sleep 1;
    
    kill -0 $pid
    stat=$?
    if [ $stat -eq 0 ] ; then
      echo "running as $!"
      exit 0
    else
      echo "$! did not start"
      exit 1
    fi
    

    也许如果你的超快速 C++ 程序不能等待一秒钟,它也不能期望能够以每秒高的速率启动大量的 shell 命令?

    也许你需要在这里实现一个队列?

    抱歉,问题多于答案

    【讨论】:

    • 如果进程在那一秒内合法地终止,这将失败。
    • @EtanReisner 对,好点。没有睡眠的问题是,如果正在启动的程序不存在,那么 kill -0 仍然会成功,因为它表示 shell 仍在试图找到要运行的程序(至少在我的测试中是这样)跨度>
    • 你甚至不需要等待; cmd & kill -0 $!。在调用 kill 之前可以成功完成的作业仍然存在竞争条件,但我怀疑任何真正的作业都会运行足够长的时间来忽略它。
    猜你喜欢
    • 2017-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 2015-08-25
    相关资源
    最近更新 更多