【问题标题】:getting the pid of a subshell command获取子shell命令的pid
【发布时间】:2016-01-15 20:53:09
【问题描述】:

我正在尝试为 java 程序编写一个初始化服务脚本。我在初始化脚本中有以下内容。

$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。

无论如何我可以构造命令以便我得到子shell命令pid吗?

谢谢!

【问题讨论】:

  • $USER = awesomeuser -- 这真的是bash吗?看起来像 PHP。
  • 您需要学习基本的bash 语法。变量赋值就像USER=awesomuser——变量前没有$,=周围没有空格。
  • 那是不是子shell。那是sub process which happens to be a shell。

标签: bash shell init pid subshell


【解决方案1】:

代替

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-ing 可以通过sudo 的正确配置来避免(参见sudo(1))。但对我来说,设置用户并运行另一个 shell 似乎更容易。

在我看来,这种方法比通过ps | grep | blabla 获取进程的 pid 更好,因为它避免了竞争条件:一旦 shell 运行了后台进程,获取 pid 的唯一正确方法是打印 $! 变量。否则我们可能会得到一个错误程序的 pid,或者如果 java 快速终止,甚至根本没有得到任何 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"

【讨论】:

    【解决方案2】:

    如何使用另一个脚本:

    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
    

    【讨论】:

    • 运行良好,但可能存在竞争条件:在调用此序列时进程可能已经死亡。
    • ps | grep name 很有可能产生错误的进程。
    • @msw 你是对的。如果 java 启动两次,我会检索到不正确的 pid。通过在 java 调用中包含时间戳,ps 将有更好的机会识别正确的进程。
    【解决方案3】:

    如果没有 shell 支持,您将无法做到这一点,而且 bash 没有提供正确执行此操作的原语。更改有趣的进程以输出或存储自己的 PID 是正确的方法。

    如果您无法更改应用程序代码,您可以使用俗气的包装器,例如:

    #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。

    【讨论】:

    • 你在下面查看我的答案了吗?为什么你认为 shell 不能用来做正确的事?
    猜你喜欢
    • 1970-01-01
    • 2018-02-05
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多