Вопрос или проблема
Я не системный администратор (я разработчик программного обеспечения) и у меня есть следующий вопрос.
Выполняя эту команду, я могу увидеть список первых 10 процессов, работающих на моей системе (исправьте меня, если я ошибаюсь):
[email protected] [~/FOLDER]# ps aux | head -10
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 191292 3892 ? Ss 2021 21:29 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0.0 0.0 0 0 ? S 2021 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< 2021 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 2021 2:17 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? S 2021 0:10 [migration/0]
root 8 0.0 0.0 0 0 ? S 2021 0:00 [rcu_bh]
root 9 0.1 0.0 0 0 ? S 2021 71:37 [rcu_sched]
root 10 0.0 0.0 0 0 ? S< 2021 0:00 [lru-add-drain]
root 11 0.0 0.0 0 0 ? S 2021 0:56 [watchdog/0]
Так что второй столбец – это PID, идентифицирующий процесс (я могу использовать его, чтобы завершить процесс).
Затем я попытался выполнить эту команду, чтобы получить все работающие процессы, связанные с JAVA:
[email protected] [~/FOLDER]# ps aux | grep java
webadmin 22316 0.0 0.0 112812 980 pts/0 S+ 18:21 0:00 grep --color=auto java
По сути, я фильтрую только строку, связанную с java.
И вот вопрос. PID всегда находится во втором столбце?
Можно ли добавить заголовок, содержащий имя столбца, как в первом примере?
Краткий ответ: не используйте ps aux
для этого.
Обычно такие процессы запускаются как сервисы, и их PID отслеживается менеджером служб (например, systemd; Upstart; supervisord; Monit…). Если цель заключается в том, чтобы проверить, работает ли служба, – менеджер службы может на это ответить. (Например, systemctl is-active MyApp
.) Если цель – перезапустить службу, если она не работает, или предотвратить одновременный запуск службы – это задача менеджера служб.
В ситуациях, когда процесс нельзя запустить как службу, его PID должен быть немедленно записан при запуске в файл PID – это может сделать либо само приложение, либо загрузчик. (Например, java -jar myapp.jar & echo $! > java.pid
.)
Когда вам действительно нужно найти все работающие процессы, используйте команду pgrep
. Например, чтобы найти все процессы Java, используйте pgrep java
, а чтобы найти все процессы, упоминающие MyApp.jar, используйте pgrep -f MyApp.jar
. (Добавьте опцию -a
, чтобы увидеть команду, например: pgrep -a java
.) Это не вызовет проблему с возвращением самого grep, как это делает ps|grep
.
Если вы абсолютно должны использовать ps
, то уточните точные столбцы, которые вы хотите. Теперь опция u
уже запрашивает конкретный формат с определенными столбцами в определенном порядке – но если вам нужен только PID, опция o
позволяет получить только PID. Например, ps axo pid,cmd
всегда возвращает ровно два столбца: PID и команду, в этом порядке.
Чтобы найти только PID, pgrep
часто лучше, чем ps
.
Теперь я предполагаю, что вы точно знаете, что хотите, вы действительно хотите ps
и хотите заголовок. Эта задача может быть решена с помощью sed
:
ps aux | sed -n '1p; /java/p'
Обратите внимание, что ваш ps … | grep …
вероятно, находит сам grep
. Аналогично, моя команда находит sed
. Вы можете избежать этого, используя регулярное выражение, эквивалентное java
, но не содержащее java
. Пример:
ps aux | sed -n '1p; /[j]ava/p'
Если вы хотите использовать grep
(например, из-за его способности добавлять цвет), может показаться, что следующее должно работать:
# недостаток, не используйте это
ps aux | { head -n 1; grep '[j]ava'; }
Это нецелесообразно, потому что head
может прочитать слишком много, и нет возможности вернуть излишне прочитанные данные, чтобы grep
смог их считать. read
в этом плане лучше, он читает по одному символу за раз, поэтому может надежно прочитать ровно одну строку:
ps aux | (IFS= read -r line; printf '%s\n' "$line"; grep '[j]ava')
Обратите внимание, что я принудительно создал подпроцесс, так что переменная умирает вместе с ним, и основная оболочка не затрагивается*.
* Оболочки могут запускать последнюю часть конвейера не в подпроцессе или неявно в подпроцессе. read line
не в подпроцессе установит переменную line
в основной оболочке. А что если переменная уже существует и содержит важное значение? Принуждая к созданию подпроцесса, мы защищаем состояние основной оболочки, независимо от того, как будет вести себя основная оболочка.
Ответ или решение
Для корректного извлечения PID процесса, связанного с Java, с помощью команды ps aux
, можно использовать несколько подходов. Давайте рассмотрим их по порядку.
1. Извлечение PID процесса Java
Чтобы увидеть список всех запущенных процессов Java, можно использовать следующую команду:
ps aux | grep '[j]ava'
В этой команде использование [j]ava
помогает избежать ситуации, когда команда grep
сама появляется в выводе. Это связано с тем, что при поиске строки "java" сама команда grep java
также будет показана, что не является желаемым результатом.
2. Заголовок с именами столбцов
Если вам нужно сформировать вывод так, чтобы он включал заголовки, вы можете сделать это с помощью утилиты sed
или через другой метод. Вот пример:
ps aux | sed -n '1p; /[j]ava/p'
В данной команде sed
выводит первую строку (заголовок) и затем строки, содержащие "java".
3. Использование команды pgrep
Если ваша основная цель – только получить PID, более предпочтительно использовать команду pgrep
, которая предназначена именно для этой цели:
pgrep java
Эта команда вернет только PID всех процессов, связанных с Java, и не будет включать саму команду поиска в результаты.
4. Уточнение вывода с ps
и добавление заголовков
Если вы хотите использовать ps
и если вам нужно вывести только определенные столбцы (например, PID и команду), вы можете сделать это следующим образом:
ps -eo pid,cmd | grep '[j]ava'
Здесь -eo pid,cmd
указывает на вывод только определенных столбцов с PID и командой.
5. Оптимизированный вывод с заголовками
Если вы хотите оставить заголовки в выводе, вы можете использовать следующий подход с grep
и head
:
{ ps -eo pid,cmd | head -n 1; ps -eo pid,cmd | grep '[j]ava'; }
Этот подход работает правильно, поскольку сначала выводится строка заголовка, а затем результаты поиска.
Заключение
На практике извлечение PID процессов Java может быть сделано несколькими способами, включая использование pgrep
для простоты и прямоты. Если вам необходимо учитывать заголовки, использование комбинаций с sed
или head
может помочь. Помните, что старайтесь избегать утечек памяти, связанных с отображением ненужных строк, таких как сам grep
.