后台进程在Linux

时间:2016-03-26 18:35:56

标签: linux bash shell

我有一个脚本启动程序(bash)在后台执行Python脚本,所以我可以启动它然后关闭terminal / ssh连接,让脚本工作。

它接受要运行的脚本的名称以及传递的可选参数。然后它启动Python脚本(分离)并在同一目录中创建一个带有PID(Python脚本)的文件,这样我以后可以重新连接到服务器并使用此文件中的PID终止此后台进程。

此PID文件用于防止相同的脚本已经运行(单例)。

问题是我无法弄清楚在Python脚本完成其工作后如何删除此PID文件。我需要在bash脚本中实现它,没有Python解决方案(因为我想在所有情况下使用它)或屏幕工具。这个主管(在脚本完成工作后将删除PID文件)也应该在后台运行(!),所以我可以做同样的事情:关闭终端会话。

到目前为止我尝试过:

#!/bin/bash

PIDFILE=$1.pid

if [ -f $PIDFILE ]; then
    echo "Process is already running, PID: $(< $PIDFILE)"
    exit 1
else
    nohup python $1 "${@:2}" > /dev/null 2>&1 &
    PID=$!
    echo $PID > $PIDFILE
    # supervisor
    nohup sh -c "wait $PID; rm -f $PIDFILE" > /dev/null 2>&1 &
fi

在此示例中,PID文件立即被删除,因为 wait 命令立即返回(我认为这是因为新进程不是当前进程的子进程,所以等待在这种情况下不起作用,如我所料。)

您对如何实施有任何想法吗? 基本上,我需要一些东西来替换这一行

nohup sh -c "wait $PID; rm -f $PIDFILE" > /dev/null 2>&1 &

将等到上一个脚本(在本例中为Python)完成其工作,然后删除PID文件。

UPD:好的,问题在于等待命令,因为它无法等待非子进程。工作解决方案是用 while 循环替换它:

#!/bin/bash

function cleanup {
    while [ -e /proc/$1 ]; do
        sleep 1;
    done
    rm -f $PIDFILE
}

PIDFILE=$1.pid

if [ -f $PIDFILE ]; then
    echo "Process is already running, PID: $(< $PIDFILE)"
    exit 1
else
    python $1 "${@:2}" > /dev/null 2>&1 &
    PID=$!
    echo $PID > $PIDFILE
    cleanup $PID > /dev/null 2>&1 &
    disown
fi

1 个答案:

答案 0 :(得分:1)

对于shell脚本,请使用traps

#!/bin/bash
function finish {
    wait $PID
    rm $PIDFILE > /dev/null 2>&1 &
}

trap finish            EXIT
trap "exit 2; finish"  SIGINT

PIDFILE=$1.pid

if [ -f $PIDFILE ]; then
    echo "Process is already running, PID: $(< $PIDFILE)"
    exit 1
else
    nohup python $1 "${@:2}" > /dev/null 2>&1 &
    PID=$!
    echo $PID > $PIDFILE
fi

Traps允许您捕获signals并对其进行响应,因此在上面的代码中, EXIT 信号(正常完成)将执行完成,删除$PIDFILE。在 SIGINT (用户请求使用ctrl-c退出)时,脚本将删除$PIDFILE并以2退出。

直接在python中:如果你想手动处理它,请看看atexit。我没有查看源代码,但看起来它实现traps以便注册清理函数:

import atexit
import os


def cleanup():
    os.unlink(pidfile)


atexit.register(cleanup)

或者自动化pidfile处理checkout pid,它将自行处理阻止同时执行:

from pid import PidFile

with PidFile():
  do_something()

或更好

from pid.decorator import pidfile

@pidfile()
def main():
  pass

if __name__ == "__main__":
  main()