Devin.KR
로그인

리눅스 pkill 명령어 사용법 - pgrep으로 먼저 확인하고 안전하게 죽이기

개발자 조회 2

이 명령어를 언제 쓰는가

ps -ef | grep worker로 PID를 찾고, 그 PID를 kill에 넘기는 두 단계를 한 번에 줄여 주는 명령이다. 같은 이름의 프로세스가 여러 개 떠 있을 때 특히 편하다.

편한 만큼 위험하다. kill은 잘못 쳐도 프로세스 하나가 죽지만, pkill은 잘못 친 패턴 하나로 수십 개를 한꺼번에 날린다. 그래서 실무에서는 pgrep으로 대상을 눈으로 확인한 뒤 같은 조건을 pkill에 그대로 넘기는 것을 규칙처럼 지킨다. pgrep과 pkill은 옵션 문법이 동일해서 명령 이름만 바꾸면 된다.

기본 형식

pgrep [매칭옵션] 패턴        # 먼저 확인
pkill [-시그널] [매칭옵션] 패턴   # 그다음 실행

패턴은 기본적으로 프로세스 이름에 대한 확장 정규식 부분 일치다. 여기서 프로세스 이름은 커널이 갖고 있는 comm 값이고 최대 15자로 잘린다. 인자까지 보고 싶으면 -f를 붙여야 한다.

자주 쓰는 옵션

옵션의미예시
-f이름이 아니라 전체 명령줄과 매칭. 강력하지만 가장 위험하다pgrep -f 'python3 worker.py'
-x부분 일치가 아니라 정확히 일치하는 것만pkill -x nginx
-u 사용자해당 사용자의 프로세스로 한정pkill -u deploy -x gunicorn
-a(pgrep) PID와 함께 전체 명령줄을 출력pgrep -a -f gunicorn
-l(pgrep) PID와 프로세스 이름을 함께 출력pgrep -l java
-c(pgrep) 개수만 센다pgrep -c -x nginx
-n / -o매칭된 것 중 가장 최근 / 가장 오래된 하나만pkill -o -x chrome
-d 구분자(pgrep) PID를 구분자로 이어서 출력pgrep -d, -f app.jar
-e(pkill) 실제로 시그널을 보낸 대상을 출력pkill -e -f worker.py
--signal보낼 시그널 지정(기본 TERM)pkill --signal HUP -x rsyslogd

실전 예제

1) 죽이기 전에 반드시 확인한다. 이 두 줄이 한 세트다. 첫 줄의 결과가 예상과 같을 때만 두 번째 줄을 친다.

pgrep -a -f 'python3 worker.py'
pkill -f 'python3 worker.py'
18342 python3 worker.py --queue=mail
18351 python3 worker.py --queue=push

2) 특정 사용자의 애플리케이션만 정리한다. 여러 서비스가 한 서버에 올라가 있을 때, 사용자 조건을 함께 걸어 사고 범위를 좁힌다.

pkill -u deploy -f 'gunicorn: master'

3) 데몬에게 설정 리로드를 지시한다. PID 파일 경로를 몰라도 되고, -x로 이름이 정확히 같은 것만 노린다.

pkill --signal HUP -x rsyslogd

4) 지정 시간 이상 살아 있는 좀비 배치를 정리한다. pgrep으로 후보를 뽑고 ps로 경과 시간을 확인한 뒤 판단한다.

ps -o pid,etime,cmd -p $(pgrep -d, -f 'batch_sync.sh')

5) 정상 종료를 먼저 시도한다. pkill도 기본은 TERM이다. 그래도 안 죽는 것만 골라 다시 친다.

pkill -e -f 'batch_sync.sh'
sleep 10
pkill -9 -e -f 'batch_sync.sh'

함정과 주의점

-f는 생각보다 훨씬 많이 잡는다. pkill -f java는 명령줄에 "java"라는 글자가 들어간 모든 프로세스를 죽인다. Java 애플리케이션 전부는 물론이고 tail -f /var/log/java-app.log까지 대상이 된다. 운영 서버에서 이 한 줄로 서비스 전체가 내려간 사례가 흔하다. -f를 쓸 때는 패턴을 최대한 길게 잡고(-f 'java -jar /opt/myapp/app.jar'), 반드시 pgrep으로 먼저 세어 본다.

패턴은 부분 일치이고 정규식이다. pkill python은 python2, python3, python3.11 을 모두 죽인다. pkill ssh는 sshd까지 죽여서 원격 접속이 끊긴다. 정확히 그 이름만 노리려면 -x를 붙인다. 또 패턴은 정규식으로 해석되므로 .이나 +가 들어간 이름은 의도와 다르게 매칭된다.

root로 조건 없이 실행하면 전 사용자가 대상이다. 일반 사용자로 실행하면 자기 소유 프로세스만 죽지만, root는 제한이 없다. root 셸에서 pkill을 칠 때는 -u로 범위를 좁히는 걸 기본으로 삼는다.

pkill은 자신을 죽이지 않지만 부모는 죽인다. pkill 프로세스 자체는 매칭에서 제외되지만, 이 pkill을 호출한 셸 스크립트는 제외되지 않는다. deploy.sh 안에서 pkill -f deploy를 치면 스크립트가 자기 자신을 죽이고 뒷부분이 실행되지 않는다.

killall과 헷갈리지 마라. 리눅스의 killall은 pkill과 비슷하게 이름으로 죽이지만, Solaris나 일부 유닉스의 killall은 모든 프로세스를 죽여 시스템을 내린다. 여러 플랫폼을 오가는 스크립트에는 pkill을 쓰는 편이 안전하다.

함께 보면 좋은 명령어

  • pgrep — pkill의 안전판. 항상 짝으로 쓴다.
  • kill — 대상이 하나로 특정될 때는 PID를 직접 지정하는 쪽이 언제나 안전하다.
  • ps — pgrep이 뽑은 PID를 넘겨 경과 시간과 전체 명령줄을 확인한다.