8

Найти и завершить процесс в одну строку с помощью bash и regex

1

Я часто сталкиваюсь с необходимостью 종료ить процесс во время программирования.

В настоящее время я использую следующий способ:

[~]$ ps aux | grep 'python csp_build.py'
user    5124  1.0  0.3 214588 13852 pts/4    Sl+  11:19   0:00 python csp_build.py
user    5373  0.0  0.0   8096   960 pts/6    S+   11:20   0:00 grep python csp_build.py
[~]$ kill 5124

Как я могу автоматически извлекать идентификатор процесса и завершать его в одной строке?

Например, чтобы сделать что-то подобное:

[~]$ ps aux | grep 'python csp_build.py' | kill <регулярное выражение, возвращающее pid>

5 ответ(ов)

17

В bash, используя только основные инструменты, указанные в вашем вопросе^(1)^, вы можете сделать следующее:

kill $(ps aux | grep '[p]ython csp_build.py' | awk '{print $2}')

Пояснения к работе этой команды:

  • Команда ps выводит список всех процессов.
  • grep фильтрует этот список по вашей строке поиска; использование [p] — это трюк, который позволяет избежать захвата самого процесса grep.
  • awk извлекает второй столбец каждой строки, который соответствует PID процесса.
  • Конструкция $(x) означает выполнить x, затем взять его выходные данные и вставить их в командную строку. Выход из этого конвейера ps в данной конструкции — это список идентификаторов процессов, в результате чего у вас получится команда типа kill 1234 1122 7654.

Вот пример, который демонстрирует это в действии:

pax> sleep 3600 &
[1] 2225
pax> sleep 3600 &
[2] 2226
pax> sleep 3600 &
[3] 2227
pax> sleep 3600 &
[4] 2228
pax> sleep 3600 &
[5] 2229
pax> kill $(ps aux | grep '[s]leep' | awk '{print $2}')
[5]+  Terminated              sleep 3600
[1]   Terminated              sleep 3600
[2]   Terminated              sleep 3600
[3]-  Terminated              sleep 3600
[4]+  Terminated              sleep 3600

В результате вы видите, что все процессы sleep были завершены.

Теперь подробнее о том, как работает часть grep '[p]ython csp_build.py': когда вы запускаете sleep 3600 &, а затем выполняете ps -ef | grep sleep, вы обычно получаете два процесса, в которых есть sleep: сам процесс sleep 3600 и процесс grep sleep (поскольку в обеих командах содержится sleep).

Тем не менее, использование ps -ef | grep '[s]leep' не создаёт процесс grep с sleep в нём; вместо этого он создаёт процесс с командой grep '[s]leep', и вот в чём фокус: grep не находит этот процесс, потому что ищет регулярное выражение "любой символ из класса символов [s] (что фактически просто s) с последующим leep.

Другими словами, он ищет sleep, но процесс grep это grep '[s]leep', который не содержит текста sleep.

Когда мне это показали (кто-то здесь на SO), я сразу начал использовать этот подход, потому что

  • это на один процесс меньше, чем добавление | grep -v grep; и
  • это элегантно и хитро, что является редким сочетанием 😃

(1) Если вам не требуется ограничиваться только базовыми инструментами, существует команда pgrep, которая находит процессы на основе определённых критериев (при условии, что она доступна на вашей системе).

Например, вы можете использовать pgrep sleep, чтобы получить идентификаторы процессов для всех команд sleep (по умолчанию она ищет по имени процесса). Если вы хотите сопоставить всю командную строку, как это показано в ps, вы можете сделать что-то вроде pgrep -f 'sleep 9999'.

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

Вы можете проверить, что процессы соответствуют вашим интересам, используя -a для отображения полных имен процессов. Также вы можете ограничить область поиска вашими собственными процессами (или конкретным набором пользователей) с помощью параметров -u или -U. Более подробную информацию можно найти в man-странице для pgrep/pkill.

Как только вы убедитесь, что команда будет отображать только интересующие вас процессы, вы можете использовать pkill с теми же параметрами, чтобы отправить сигнал всем этим процессам.

1

Если у вас установлен pkill, вы можете использовать следующую команду для завершения всех процессов, связанных с csp_build.py:

pkill -f csp_build.py

Если вы хотите фильтровать только по имени процесса, а не по полному списку аргументов, просто уберите флаг -f. Таким образом команда будет выглядеть так:

pkill csp_build.py

Это позволит завершить процессы, которые точно совпадают с указанным названием, игнорируя параметры и аргументы.

1

Однострочник:

ps aux | grep -i csp_build | awk '{print $2}' | xargs sudo kill -9

  • Выводит 2-й столбец: awk '{print $2}'
  • sudo - опционально
  • Запускает kill -9 5124, kill -9 5373 и т.д. (kill -15 более аккуратный, но чуть медленнее)

Бонус:

У меня также определены 2 функции в моем .bash_profile (~/.bash_profile предназначен для osx, вам нужно посмотреть, что работает на вашей *nix машине).

  1. p ключевое_слово
    • отображает все Процессы, содержащие ключевое слово
    • использование, например: p csp_build, p python и т.д.

код для bash_profile:

# ПОИСК ПРОЦЕССА
function p(){
        ps aux | grep -i $1 | grep -v grep
}
  1. ka ключевое_слово
    • Уб****ивает Автоматычески все процессы, содержащие это ключевое слово
    • использование, например: ka csp_build, ka python и т.д.
    • опциональный уровень убивания, например: ka csp_build 15, ka python 9

код для bash_profile:

# УБИВАЕТ ВСЕ
function ka(){

    cnt=$( p $1 | wc -l)  # общее количество найденных процессов
    klevel=${2:-15}       # уровень убивания, по умолчанию 15, если 2-й аргумент пуст

    echo -e "\nПоиск '$1' -- Найдено" $cnt "Запущенных процессов .. "
    p $1

    echo -e '\nЗавершение' $cnt 'процессов .. '

    ps aux  |  grep -i $1 |  grep -v grep   | awk '{print $2}' | xargs sudo kill -$klevel
    echo -e "Готово!\n"

    echo "Повторный поиск:"
    p "$1"
    echo -e "\n"
}
0

Команда killall -r regexp используется для завершения процессов, имена которых соответствуют заданному регулярному выражению.

Флаг -r или --regexp интерпретирует шаблон имени процесса как расширенное регулярное выражение. Это позволяет более гибко задавать критерии для поиска процессов, которые вы хотите завершить.

Например, если вы хотите завершить все процессы, названия которых содержат "example", вы можете использовать следующую команду:

killall -r '.*example.*'

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

0

Это вернёт только идентификатор процесса (PID):

pgrep -f 'process_name'

Чтобы завершить любой процесс за одну команду, используйте:

kill -9 $(pgrep -f 'process_name')

Или, если вы знаете точное имя процесса, вы также можете использовать pidof:

kill -9 $(pidof 'process_name')

Но если вы не знаете точное имя процесса, лучше использовать pgrep.

Если есть несколько процессов с одинаковым именем и вы хотите завершить только первый, выполните:

kill -9 $(pgrep -f 'process_name' | head -1)

Также стоит отметить, что если вас беспокоит регистронезависимость, вы можете добавить опцию -i, как в grep. Например:

kill -9 $(pgrep -fi chrome)

Больше информации о сигналах и pgrep можно найти в документации: man 7 signal, man signal или man pgrep.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь