获取子shell命令的pid

时间:2016-01-15 20:53:09

标签: bash shell init pid subshell

我正在尝试为java程序编写init服务脚本。我在init脚本中有以下内容。

$USER = awesomeuser

$PROGRAM_CMD = "java -server com.test.TestClass"

$PROGRAM_LOG = "/var/log/awesome_log"

sudo -u $USER nohup $PROGRAM_CMD >> $PROGRAM_LOG 2>&1 </dev/null &
server_pid=$!
echo $server_pid > $pidfile

发生的事情是我正在获取父进程的pid,但我真的希望从子shell中运行java进程的pid。

无论如何我可以构造命令,所以我得到了subshel​​l命令pid吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

而不是

sudo -u $USER nohup $PROGRAM_CMD >> $PROGRAM_LOG 2>&1 </dev/null &

试试这个:

sudo -u $USER bash -c "nohup $PROGRAM_CMD >> $PROGRAM_LOG 2>&1 & </dev/null; echo "'$!'

sudo通常会生成子进程并在其中调用exec。通过fork的正确配置可以避免sudo - (请参阅sudo(1))。但是我似乎更容易设置用户并运行另一个shell。

这种方法似乎比通过ps | grep | blabla获取进程的pid更好,因为它避免了竞争条件:一旦shell运行了后台进程,获取pid的唯一正确方法是打印$!变量。否则,如果java很快终止,我们就会得到一个错误的程序的pid,甚至根本就没有pid。

顺便说一句,为了给变量分配适当的值,脚本的起始行应该是

USER=awesomeuser
PROGRAM_CMD="java -server com.test.TestClass"
PROGRAM_LOG="/var/log/awesome_log"

而不是

$USER = awesomeuser
$PROGRAM_CMD = "java -server com.test.TestClass"
$PROGRAM_LOG = "/var/log/awesome_log"

答案 1 :(得分:1)

使用其他脚本的原因是什么:

findpid.sh

#!/bin/bash
pid=""
pidfile="pid.log"

while [ -z "${pid}" ]; do
    pid=`ps -U "${1}" | grep "${2}" | xargs | cut -d" " -f 1`
done

echo "${pid}" > ${pidfile}

然后在脚本中调用findpid.sh。您可以在调用java时使用时间戳,以确保使用ps标识正确的进程。

#!/bin/bash
USER=awesomeuser
TIME=`date +"%s"`
PROGRAM_CMD="java -server com.test.TestClass java -Dtime=${TIME}"
PROGRAM_LOG="awesome_log"

bash findpid.sh "${USER}" "${PROGRAM_CMD}" &
sudo -u $USER nohup $PROGRAM_CMD >> $PROGRAM_LOG 2>&1 </dev/null &
wait

答案 2 :(得分:1)

如果没有shell支持,你就无法做到这一点,而bash并没有提供正确的原语。改变有趣的过程来输出或存储自己的PID是正确的方法。

如果您无法更改应用程序代码,可以使用像:

这样的cheezy包装器
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

/*
 * pidder cmd args - launch cmd with args and print its pid
 */

int
main(int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "usage: pidder command [args]\n");
        exit(2);
    }

    switch(fork()) {
        case -1:
            perror("fork");
            exit(2);
        case 0:
            printf("%d\n", getpid());
            /* launch cmd with path search if relative path */
            execvp(argv[1], argv+1);
            perror("exec");
            exit(1);
        default:
            exit(0);
    }

}

如果你使用它,请注意$ ./pidder some_command$ some_command有细微差别,一个简单的例子就是在第一种情况下,PPID不会是调用shell。