【问题标题】:Cleanup after the background process finished its work on Linux后台进程在 Linux 上完成工作后的清理
【发布时间】:2016-03-26 18:35:55
【问题描述】:

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

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

此外,此 PID 文件用于防止相同的脚本在已经运行(单例)时被启动。

问题是我不知道在 Python 脚本完成工作后如何删除这个 PID 文件。我需要在 bash 脚本中实现它,没有 Python 解决方案(因为我想在所有情况下都使用它)或 screen 工具。这个主管(将在脚本完成工作后删除 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 命令立即返回(我认为这是因为新进程不是当前进程的子进程,所以 wait strong> 在这种情况下不起作用)。

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

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

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

UPD:好的,问题出在 wait 命令上,因为它不能等待非子进程。可行的解决方案是用 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

【问题讨论】:

    标签: linux bash shell


    【解决方案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
    

    陷阱允许你捕获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()
    

    【讨论】:

    • 这个解决方案被锁定到 Python,但我不仅会运行 Python 脚本,所以我需要在 Bash 中实现一些东西
    • 啊,抱歉,等一下。
    • 这段代码的问题是它会阻塞,直到 Python 脚本完成它的工作。我也需要这个主管在后台工作,所以这个脚本启动器应该几乎立即完成它的工作(启动 Python 脚本,启动主管,退出)。
    • 这段代码没有问题,你需要单独实现。您可以使用subprocess.Popen(["/bin/mycmd", "myarg"]).pid 来允许您启动后台进程。您需要做的就是在 atexit 调用的方法或您的案例中的主要操作中应用它。
    • 对于 bash,您需要做的就是将包装器置于后台,但这听起来确实像是您应该在主脚本中处理的事情。如果您使用的是主管,您只需将 numprocs 设置为 1 就可以了,无需手动管理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多