Як отримати ідентифікатор процесу фонового процесу?


375

Я запускаю фоновий процес зі свого сценарію оболонки, і я хотів би вбити цей процес, коли мій сценарій закінчиться.

Як отримати PID цього процесу з мого сценарію оболонки? Наскільки я бачу, змінна $!містить PID поточного сценарію, а не фоновий процес.


8
$! правильно. Ви впевнені, що починаєте сценарій в BG? зразок, будь ласка.
піксельбіт

7
Так $! правильно. Я помилявся.
Володимир Безуглий

11
$$ містить поточний PID сценарію.
HUB

Відповіді:


583

Вам потрібно зберегти PID фонового процесу під час його запуску:

foo &
FOO_PID=$!
# do other stuff
kill $FOO_PID

Ви не можете використовувати контроль роботи, оскільки це інтерактивна функція та прив’язана до керуючого терміналу. Сценарій не обов'язково має бути приєднаний термінал, тому контроль за роботою не обов’язково буде доступним.


22
З $! повертає pid останнього фонового процесу. Чи можливо, що щось починається між fooі $!, і ми отримуємо це щось pid замість foos?
WiSaGaN

53
@WiSaGaN: Ні. Між цими рядками немає нічого. Будь-яка інша діяльність в системі не вплине на це. $! розшириться до PID останнього фонового процесу в цій оболонці .
camh

8
... який шлангує вас, якщо fooтрапляється кілька команд з трубопроводами (наприклад tail -f somefile.txt | grep sometext). У таких випадках ви отримаєте PID команди grep $!замість хвостової команди, якщо це те, що ви шукали. Вам потрібно буде використовувати jobsабо psвподобання в цьому випадку.
Джон Рікс

1
@JohnRix: Не обов’язково. Ви отримаєте під grep, але якщо ви вб'єте це, хвіст отримає ПІДПИС, коли він спробує записати в трубу. Але як тільки ви намагаєтеся потрапити в будь-який складний процес управління / контролю, bash / shell стає досить болісним.
camh

5
Ще одне гідне рішення пропонується в (коментар до відповіді) Як зрозуміти щойно розпочатий процес : о, і "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 листопада '10 о 14:28
imz - Іван Захарящев

148

Ви можете скористатися jobs -lкомандою, щоб потрапити на певну роботуL

^Z
[1]+  Stopped                 guard

my_mac:workspace r$ jobs -l
[1]+ 46841 Suspended: 18           guard

У цьому випадку 46841 є PID.

Від help jobs:

-l Повідомте про ідентифікатор групи процесів та робочий каталог завдань.

jobs -p є ще одним варіантом, який показує лише PID.


Щоб використовувати це в сценарії оболонки, вам доведеться обробити висновок.
Філ

6
@Phil Щоб вказати лише підказки: jobs -p. Щоб переглянути список певної роботи: робочі місця -p% 3. Не потрібно обробляти вихід.
Ерік Аронесті

1
@Erik за допомогою різних команд / аргументів ви змінюєте контекст мого коментаря. Без додаткового аргументу запропонований результат вимагає обробки. Запропонуйте покращити відповідь!
Філ

Збереження PID $!після того, як ви його почали, він більш портативний і простіший у більшості ситуацій. Ось що робить прийнята на даний момент відповідь.
tripleee

jobs -pповернув те саме, що і jobs -lна Lubuntu 16.4
Тимо

46
  • $$ - поточний номер сценарію
  • $! - привід останнього фонового процесу

Ось зразок стенограми сеансу bash ( %1посилається на порядковий номер фонового процесу, як видно з jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100

відлуння %1не повертає фоновий процес на моєму Ubuntu а echo $!робить
Timo

25

Ще простіший спосіб знищити весь дочірній процес bash-сценарію:

pkill -P $$

-PПрапор працює так само , як з pkillі pgrep- він отримує дочірні процеси, тільки pkillдочірні процеси вбивають і pgrepдитини ИДП друкується на стандартний висновок.


Дуже зручно! Це найкращий спосіб гарантувати, що ви не залишаєте відкритих процесів на задньому плані.
лепе

@lepe: Не зовсім. Якщо ви бабуся і дідусь, це не спрацює: після bash -c 'bash -c "sleep 300 &"' & бігу pgrep -P $$нічого не показує, тому що сон не буде прямою дитиною вашої оболонки.
Петре

1
@AlexeyPolonsky: так має бути: вбийте всі дочірні програми оболонки, а не сценарій. Тому що $$стосується поточної оболонки.
Тимо

виконуючи bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, я потрапляю на stdout [1] <pid>. So at least it shows something, but this is probably not the output of pgrep`
Тімо

Навіть процеси можуть зняти їх з батьківського процесу. Простий фокус - зателефонувати на вилку (створити онука), а потім просто дати дитині процес виходу, поки онук продовжує виконувати роботу. (демонізація - це ключове слово) Але навіть якщо дитина продовжує працювати занадто pkill -P недостатньо для поширення сигналу онукам. Такий інструмент, як pstree, необхідний для керування усім залежним деревом процесу. Але це не сприймає демонів, розпочатих із процесу, оскільки їх батько є процесом 1. Наприклад:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Паулі Ніємінен

4

це те, що я зробив. Перевірте це, сподіваємось, що це може допомогти.

#!/bin/bash
#
# So something to show.
echo "UNO" >  UNO.txt
echo "DOS" >  DOS.txt
#
# Initialize Pid List
dPidLst=""
#
# Generate background processes
tail -f UNO.txt&
dPidLst="$dPidLst $!"
tail -f DOS.txt&
dPidLst="$dPidLst $!"
#
# Report process IDs
echo PID=$$
echo dPidLst=$dPidLst
#
# Show process on current shell
ps -f
#
# Start killing background processes from list
for dPid in $dPidLst
do
        echo killing $dPid. Process is still there.
        ps | grep $dPid
        kill $dPid
        ps | grep $dPid
        echo Just ran "'"ps"'" command, $dPid must not show again.
done

Тоді просто запустіть це так: ./bgkill.shз належними дозволами звичайно

root@umsstd22 [P]:~# ./bgkill.sh
PID=23757
dPidLst= 23758 23759
UNO
DOS
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     23757  3937  0 11:55 pts/5    00:00:00 /bin/bash ./bgkill.sh
root     23758 23757  0 11:55 pts/5    00:00:00 tail -f UNO.txt
root     23759 23757  0 11:55 pts/5    00:00:00 tail -f DOS.txt
root     23760 23757  0 11:55 pts/5    00:00:00 ps -f
killing 23758. Process is still there.
23758 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23758 Terminated              tail -f UNO.txt
Just ran 'ps' command, 23758 must not show again.
killing 23759. Process is still there.
23759 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23759 Terminated              tail -f DOS.txt
Just ran 'ps' command, 23759 must not show again.
root@umsstd22 [P]:~# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     24200  3937  0 11:56 pts/5    00:00:00 ps -f

3

Ви також можете використовувати pstree:

pstree -p user

Зазвичай це текстове подання всіх процесів для "користувача", а параметр -p дає ідентифікатор процесу. Наскільки я розумію, це не залежить від того, щоб процеси були у власності поточної оболонки. Він також показує виделки.


1
Щоб використовувати це в сценарії оболонки, вам доведеться сильно обробити результат.
Філ

1

pgrepможе отримати всі дочірні PID батьківського процесу. Як згадувалося раніше, $$це поточні сценарії PID. Отже, якщо ви хочете, щоб сценарій, який очищається після себе, це слід зробити:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT

Хіба це не вбило б долю?
Філ

Так, це не було, питання ніколи не згадувало про те, щоб зберегти деяких дітей на виході після виходу.
errant.info

trap 'pkill -P $$' SIGING SIGTERM EXITвиглядає простіше, але я цього не тестував.
петре

Для сумісності не використовуйте SIGпрефікс. Це дозволено POSIX, але просто як розширення, яке можуть підтримувати реалізації : pubs.opengroup.org/onlinepubs/007904975/utilities/trap.html Оболонка тире, наприклад, не має.
Джош
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.