Як зрозуміти щойно розпочатий процес


70

Я хочу запустити процес (наприклад, myCommand) і отримати його pid (щоб потім вбити його).

Я спробував ps та фільтрувати по імені, але не можу відрізнити процес за іменами

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

Тому що назви процесів не є унікальними.

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

myCommand &

Я виявив, що я можу отримати цей PID:

echo $!

Чи є простіше рішення?

Я був би радий виконати myCommand і отримати його PID в результаті однієї рядкової команди.

Відповіді:


77

Що може бути простіше, ніж echo $!? Як один рядок:

myCommand & echo $!

Дякую, що об’єднання цих команд із "&" мені дуже допомогло.
rafalmag

8
в bash-скрипті, в циклі, який запускає програми, $! не є точним. Іноді він повертає pid самого сценарію, іноді grep або awk запускають із сценарію будь-яке рішення, щоб отримати специфіку pid процесу, щойно запущеного в цьому сценарії? щось на кшталт pid = myprogramбуло б дивним

2
Зверніть увагу, що для цього потрібно запустити команду, використовуючи &попередній рядок, інакше відлуння повернеться порожнім.
rogerdpack

2
присвоєння змінної як command & echo $!заморожує виконання на цьому кроці :(
Шашанк Вівек

26

Оберніть команду невеликим сценарієм

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file

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 нової команди. Деякі оболонки забезпечують свої власні не портативні механізми пошуку ПІД підрозділу, який ви можете використовувати для цього. Зокрема, в Bash 4 , (echo $BASHPID; exec myCommand)працює.

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


Перш ніж я можу запустити myCommand, мені потрібно встановити ряд змінних середовища в моєму скрипті bash. Чи перенесуть їх у середовище, в якому працює execкоманда?
користувач5359531

Схоже, моє оточення переходить у execкоманду. Однак такий підхід не працює, коли myCommandзапускаються інші процеси, з якими вам потрібно працювати; коли я видаю a, kill -INT <pid>де pidбуло отримано таким чином, сигнал не доходить до запущених підпроцесів myCommand, тоді як якщо я запускаюсь myCommand у поточному сеансі та Ctrl + C, сигнали поширюються правильно.
користувач5359531

1
Я спробував це, але під процесу myCommand, схоже, є результатом pid за допомогою echo $$ +1. Я щось роблю не так?
кробар

Моя команда виглядає так:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar

7

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

В якості бічної замітки замість трубопроводу з PS ви можете використовувати pgrepабо pidof.


5

використовувати exec з bash script після реєстрації pid у файлі:

приклад:

припустимо, у вас є сценарій під назвою "forever.sh", який потрібно запустити з аргументами p1, p2, p3

source.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

і назавжди.sh працює aok. syslog 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

3
о, і "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419

1
Щоб правильно зберегти внутрішню пробіл у аргументах, слід використовувати exec "$@"замість exec $*. Технічно те, що вам потрібно зберегти, це не пробіл, а виникнення символів у параметрі оболонки IFS (який за замовчуванням пробіл, вкладку та новий рядок).
Кріс Джонсен

точка взята. :)
user237419

Дякую, я не знав параметра $$. Це може бути дуже корисно.
rafalmag

3

В оболонці bash альтернативою $!може бути jobs -pвбудована. У деяких випадках !in $!отримує інтерпретацію оболонки до (або замість) розширення змінної, що призводить до несподіваних результатів.

Наприклад, це не працює:

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

поки це:

((yourcommand) & jobs -p >/var/run/pidfile)

Я думаю, ти мав на увазі ((yourcommand) & jobs -p> / var / run / pidfile).
Дом

1

Ви можете використовувати щось на кшталт:

$ myCommand ; pid=$!

Або

$ myCommand && pid=$!

Дві команди можуть бути з'єднаними, використовуючи ;або &&. У другому випадку pid буде встановлений лише у випадку успіху першої команди. Ви можете отримати ідентифікатор процесу від $pid.


3
ОП хоче отримати PID, щоб пізніше його вбити. ; і && вимагають вихідного процесу вийти перед echo $! виконується.
користувач9517

Так, ти правий. Це дасть вам під після припинення 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.


Як я можу записати номер PID, повернений цим кодом, у змінну bash? З незрозумілої мені причини, stdoutсхоже, не зафіксовано в цьому прикладі:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9

@ Tfb9: я б чесно рекомендував один із інших підходів; Я писав це ще 2 роки тому тому, і це, безумовно, один із представлених методів хаккі / помилок (або я так вважаю).
tonysdg

Дякую за замітку. Я думаю, що я вирішив свою проблему з цього sleep 10 & PID_IS=$!; echo $PID_IS
приводу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.