получение pid команды подоболочки

Я пытаюсь написать сценарий службы инициализации для 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 родительского процесса, но мне действительно нужен pid процесса java, работающего из подоболочки.

Могу ли я в любом случае структурировать команду, чтобы вернуть pid команды подоболочки?

Спасибо!


person user3418832    schedule 15.01.2016    source источник
comment
$USER = awesomeuser -- это действительно bash? Похоже на PHP.   -  person Oleg Andriyanov    schedule 16.01.2016
comment
Вам нужно изучить базовый синтаксис bash. Присвоение переменных похоже на USER=awesomuser -- без $ перед переменной и без пробелов вокруг =.   -  person Barmar    schedule 16.01.2016
comment
Это не подоболочка. Это подпроцесс , который оболочка.   -  person Franklin Yu    schedule 22.08.2018


Ответы (3)


Вместо

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)). Но мне кажется проще установить пользователя и запустить другую оболочку.

Этот подход кажется мне лучше, чем получение pid процесса с помощью ps | grep | blabla, поскольку он позволяет избежать состояния гонки: после того, как оболочка запустила фоновый процесс, единственный правильный способ получить pid — это напечатать переменную $!. В противном случае мы можем получить pid неправильной программы или вообще не получить pid, если java быстро завершится.

Кстати, чтобы присвоить правильные значения вашим переменным, начальные строки вашего скрипта должны быть

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"
person Oleg Andriyanov    schedule 15.01.2016

Как насчет использования другого скрипта:

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
person jyvet    schedule 15.01.2016
comment
Работает хорошо, но может быть состояние гонки: процесс может быть уже мертвым в момент вызова этой последовательности. - person Oleg Andriyanov; 16.01.2016
comment
ps | grep name имеет высокую вероятность получения неправильных процессов. - person msw; 16.01.2016
comment
@msw ты прав. Если java запускается дважды, может быть получен неправильный pid. Включив метку времени в вызов java, у ps будет больше шансов идентифицировать правильный процесс. - person jyvet; 16.01.2016

Вы не можете сделать это без поддержки оболочки, а 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 не будет вызывающей оболочкой.

person msw    schedule 15.01.2016
comment
Вы проверили мой ответ ниже? Как вы думаете, почему нельзя использовать оболочку, чтобы сделать это правильно? - person Oleg Andriyanov; 16.01.2016