Как получить pid только что начавшегося процесса

71

Я хочу запустить процесс (например, myCommand) и получить его PID (чтобы позволить убить его позже).

Я пробовал PS и фильтр по имени, но я не могу отличить процесс по именам

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

Потому что имена процессов не уникальны.

Я могу запустить процесс:

myCommand &

Я обнаружил, что могу получить этот PID:

echo $!

Есть ли более простое решение?

Я был бы счастлив выполнить myCommand и получить его PID в результате одной строковой команды.

rafalmag
источник

Ответы:

77

Что может быть проще чем echo $!? Одной строкой:

myCommand & echo $!
Яцек Конечны
источник
Спасибо, что объединение этих команд с "&" мне очень помогло.
Rafalmag
8
в скрипте bash, в цикле, который запускает программы, $! не является точным. Иногда он возвращает pid самого скрипта, иногда запускается grep или awk из скрипта, какое-либо решение, чтобы специально получить pid процесса, только что запущенного в этом сценарии? что-то вроде pid = myprogramбыло бы здорово
2
Обратите внимание, что для этого необходимо запустить команду, используя в &качестве предыдущей строки a , в противном случае эхо в основном возвращает пустое значение.
rogerdpack
2
присваивание переменной вроде command & echo $!останавливает выполнение на этом шаге :(
Шашанк Вивек
26

Оберните команду в небольшой скрипт

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517 поддерживает GoFundMonica
источник
20

Вы можете использовать sh -cи, execчтобы получить PID команды еще до ее запуска.

Для начала myCommand, чтобы его PID печатался до его запуска, вы можете использовать:

sh -c 'echo $$; exec myCommand'

Как это устроено:

Это запустит новую оболочку, напечатает PID этой оболочки, а затем использует execвстроенную функцию для замены оболочки вашей командой, убедившись, что она имеет тот же PID. Когда ваша оболочка запускает команду со execвстроенной функцией, ваша оболочка фактически становится этой командой , а не более распространенным поведением создания новой собственной копии, которая имеет свой собственный отдельный PID и которая затем становится командой.

Я считаю, что это намного проще, чем альтернативы, включающие асинхронное выполнение (с &), управление заданиями или поиск с помощью ps. Эти подходы хороши, но если у вас нет конкретной причины их использовать - например, возможно, команда уже запущена, и в этом случае поиск ее PID или использование управления заданиями имеет смысл - я предлагаю сначала рассмотреть этот способ. (И я бы, конечно, не подумал о написании сложного сценария или другой программы для достижения этой цели).

Этот ответ включает в себя пример этой техники.


Части этой команды иногда могут быть опущены, но не всегда.

Даже если используемая вами оболочка выполнена в стиле Борна и, следовательно, поддерживает execвстроенную функцию с этой семантикой, вам, как правило, не следует пытаться избегать использования sh -c(или его эквивалента) для создания нового отдельного процесса оболочки для этой цели, поскольку:

  • Как только оболочка стала myCommand, нет оболочки, ожидающей запуска следующих команд. sh -c 'echo $$; exec myCommand; fooне сможет попытаться запустить fooпосле замены себя myCommand. Если вы не пишете скрипт, который запускает это как последнюю команду, вы не можете просто использовать echo $$; exec myCommandв оболочке, где вы выполняете другие команды.
  • Вы не можете использовать подоболочку для этого. (echo $$; exec myCommand)может быть синтаксически лучше, чем sh -c 'echo $$; exec myCommand', но когда вы запускаете $$внутри ( ), он дает PID родительской оболочки, а не самой подоболочки. Но PID подоболочки будет PID новой команды. Некоторые оболочки предоставляют свои собственные непереносимые механизмы для нахождения PID подоболочки, которые вы можете использовать для этого. В частности, в Bash 4 , (echo $BASHPID; exec myCommand)работает.

Наконец, обратите внимание, что некоторые оболочки выполняют оптимизацию, когда они запускают команду, как если бы exec(то есть сначала отказываются от разветвления), когда известно, что оболочке не нужно будет ничего делать позже. Некоторые оболочки пытаются сделать это в любое время, когда выполняется последняя команда, в то время как другие будут делать это только тогда, когда нет других команд до или после команды, а другие вообще не будут делать это. В результате, если вы забудете написать execи просто использовать, sh -c 'echo $$; myCommand'это иногда даст вам правильный PID в некоторых системах с некоторыми оболочками. Я рекомендую никогда не полагаться на такое поведение , а вместо этого всегда включать, execкогда это то, что вам нужно.

Элия ​​Каган
источник
Прежде чем я смогу запустить myCommand, мне нужно установить несколько переменных окружения в моем скрипте bash. Будут ли они перенесены в среду, в которой выполняется execкоманда?
user5359531
Похоже, мое окружение переносится в execкоманду. Однако этот подход не работает, когда myCommandзапускаются другие процессы, с которыми вам нужно работать; когда я выдаю kill -INT <pid>местоположение, pidполученное таким образом, сигнал не достигает подпроцессов, запущенных с помощью myCommand, тогда как, если я запускаю myCommand в текущем сеансе и Ctrl + C, сигналы распространяются правильно.
user5359531
1
Я пробовал это, но pid процесса myCommand, кажется, вывод pid по echo $$ +1. Я делаю что-то неправильно?
crobar
Моя команда выглядит так:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar
7

Я не знаю ни одного более простого решения, но не использую $! достаточно хорошо? Вы всегда можете присвоить значение некоторой другой переменной, если вам это понадобится позже, как сказали другие.

В качестве примечания, вместо ps от ps вы можете использовать pgrepили pidof.

carlpett
источник
5

используйте exec из скрипта bash после регистрации pid в файле:

пример:

Предположим, у вас есть скрипт с именем "forever.sh", который вы хотите запустить с аргументами p1, p2, p3

forever.sh исходный код:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

создать reaper.sh:

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

запустите forever.sh через reaper.sh:

./reaper.sh ./forever.sh p1 p2 p3 p4 &

forever.sh не делает ничего, кроме записи строки в системный журнал каждые 5 секунд

теперь у вас есть pid в /var/run/forever.sh.pid

cat /var/run/forever.sh.pid 
5780

и навсегда. grep системного журнала:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

Вы можете увидеть это в таблице процессов:

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4
user237419
источник
3
о, и "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419
1
Чтобы правильно сохранить внутренние пробелы в аргументах, вы должны использовать exec "$@"вместо exec $*. Технически, что вам нужно сохранить, это не пробелы, а вхождения символов в параметре оболочки IFS (по умолчанию пробел, табуляция и символ новой строки).
Крис Джонсен
Дело принято. :)
user237419
Спасибо, я не знал параметра $$. Это может быть очень полезно.
Рафальмаг
3

В оболочке bash альтернативой $!может быть jobs -pвстроенная. В некоторых случаях !в $!получает интерпретатор до (или вместо) переменного расширения, что приводит к неожиданным результатам.

Это, например, не будет работать:

((yourcommand) & echo $! >/var/run/pidfile)

пока это будет:

((yourcommand) & jobs -p >/var/run/pidfile)
mustaccio
источник
Я думаю, что вы имели в виду ((ваша команда) & jobs -p> / var / run / pidfile).
Дом
1

Вы можете использовать что-то вроде:

$ myCommand ; pid=$!

Или же

$ myCommand && pid=$!

Две команды могут быть соединениями с помощью ;или &&. Во втором случае pid будет установлен только в случае успешного выполнения первой команды. Вы можете получить идентификатор процесса от $pid.

Халед
источник
3
ОП хочет получить PID, чтобы потом его убить. ; и && требует, чтобы исходный процесс завершился до echo $! выполнен.
user9517 поддерживает GoFundMonica
Да ты прав. Это даст вам pid после завершения myCommand.
Халед
6
Ссылка $!после &&или ;никогда не даст вам PID процесса, запущенного для левой части разделителя команд. $!устанавливается только для процессов, запускаемых асинхронно (например, обычно, &но в некоторых оболочках также есть другие методы).
Крис Джонсен
это полезно, и почему никто не голосует, кроме меня? Хорошая работа, хотя :)
Храм
1

Это немного хакерский ответ и вряд ли подойдет большинству людей. Это также огромный риск для безопасности, поэтому не делайте этого, если вы не уверены, что будете в безопасности, а входы очищены и ... ну, вы поняли идею.

Скомпилируйте небольшую C-программу в двоичный файл с именем start(или как хотите), затем запустите вашу программу как./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

Короче говоря, это напечатает PID stdout, а затем загрузит вашу программу в процесс. Он должен иметь тот же PID.

tonysdg
источник
Как я могу записать номер PID, возвращаемый этим кодом, в переменную bash? По непонятной мне причине, stdoutпохоже, не записано в этом примере:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9
@ Tfb9: я бы честно рекомендовал один из других подходов; Я написал это 2+ года назад, и это, безусловно, один из представленных хакерских / подверженных ошибкам методов (или я так думаю).
tonysdg
Спасибо за примечание. Я думаю, что я решил свою проблему с этимsleep 10 & PID_IS=$!; echo $PID_IS
Tfb9