Published on

linux timeout example: 리눅스 timeout 명령어 사용법

Authors
  • avatar
    Name
    Indo Yoon
    Twitter

리눅스에서 오래 걸리는 작업이 끝없이 매달리는 상황을 막는 가장 간단한 방법은 timeout 명령어로 실행 시간 상한을 거는 것입니다. 이 글은 timeout의 기본 문법과 자주 쓰는 옵션, 배포 스크립트와 cron에서 어떤 값을 골라야 하는지를 예제 중심으로 정리해요. 서버 운영 중 무한 대기로 배포가 멈추는 사고를 줄이는 데 바로 적용할 수 있습니다.

한눈에 보기

timeout은 "정해진 시간 안에 끝나지 않으면 프로세스에 신호를 보내 종료시킨다"는 단일 목적의 명령어입니다. 데몬을 따로 띄우거나 래퍼 스크립트를 만들 필요 없이, 실행하려는 명령 앞에 붙이기만 하면 됩니다. 그래서 배포 자동화나 정기 작업에서 가장 먼저 추가하는 안전장치로 쓰여요.

  • 형식은 timeout [옵션] 시간 명령어 [인자...] 입니다.
  • 시간을 지정하지 않으면 명령이 실행되지 않으므로 반드시 값을 적어야 합니다.
  • 기본으로 보내는 신호는 TERM이고, 시간 초과로 종료되면 종료 코드 124를 돌려줍니다.
  • TERM을 무시하는 프로그램에는 --kill-after로 KILL을 예약해 두는 편이 안전합니다.
  • 종료 코드를 그대로 활용하려면 --preserve-status를 함께 씁니다.

timeout 명령어 기본 문법

가장 기본이 되는 형태는 다음과 같습니다.

timeout [옵션] 시간 명령어 [인자...]

시간 값은 정수와 접미사의 조합으로 적습니다. 접미사를 생략하면 초로 해석되고, s, m, h, d를 붙이면 각각 다른 단위로 바뀝니다. 소수 형태의 시간도 받아들이지만, 스크립트를 읽는 사람을 위해 정수와 접미사를 쓰는 편이 가독성이 좋아요.

timeout 30 curl -sS https://example.com/health

위 예제는 헬스 체크 요청이 응답 없이 매달릴 때 스크립트 전체가 멈추는 것을 막습니다. 네트워크 요청은 curl 자체의 --max-time 옵션과 timeout을 함께 거는 경우가 많습니다. curl 옵션은 연결과 전송 단계를 세밀하게 제어하고, timeout은 그 바깥에서 전체 실행 시간을 제한하는 역할을 합니다.

명령이 정상 종료되면 timeout은 해당 명령의 종료 코드를 그대로 전달합니다. 반면 시간 초과로 끊겼을 때는 124를 반환하므로, 스크립트에서 두 경우를 구분해 로그를 남길 수 있어요.

주요 옵션 비교 표

옵션역할주로 쓰는 상황
-s, --signal보낼 신호를 지정합니다. 기본값은 TERM입니다.애플리케이션의 정상 종료 처리를 유도하고 싶을 때
-k, --kill-after신호를 보낸 뒤에도 프로세스가 남아 있으면 KILL을 예약합니다.TERM을 무시하는 프로그램을 확실히 정리할 때
--preserve-status시간 초과 여부와 무관하게 명령의 실제 종료 코드를 유지합니다.종료 코드로 성공과 실패를 판정할 때
--foregroundtimeout이 별도 프로세스 그룹을 만들지 않습니다.TTY 입력이 필요한 대화형 명령을 감쌀 때
-v, --verbose종료 시 진단 메시지를 출력합니다.동작을 디버깅하거나 로그로 남길 때

표에서 알 수 있듯이 옵션은 크게 "어떤 신호를 보낼지", "얼마나 더 기다릴지", "결과를 어떻게 보고할지"로 나뉩니다. 대부분의 운영 스크립트는 -k와 --preserve-status 조합만으로도 충분합니다.

단계별 사용 방법

가장 단순한 형태로 감싸기

실행할 명령 앞에 시간 값을 붙이는 것만으로 끝납니다.

timeout 60 ./deploy.sh

이 한 줄이면 배포 스크립트가 정해진 시간을 넘길 때 자동으로 중단됩니다. 다만 스크립트 내부에서 자식 프로세스를 띄운 경우, 그 자식까지 항상 정리된다고 가정하면 안 됩니다.

종료 신호를 바꾸기

timeout -s INT 30 ./long-task.sh

INT는 터미널에서 Ctrl+C를 눌렀을 때와 같은 신호입니다. 애플리케이션이 인터럽트를 받아 정상 종료 훅을 실행하도록 설계되어 있다면, TERM보다 INT가 의도에 맞을 수 있습니다.

강제 종료 시점을 추가하기

timeout -k 10 30 ./long-task.sh

먼저 TERM을 보내고, 그 뒤에도 살아 있으면 KILL을 보냅니다. 신호를 무시하도록 작성된 프로그램이나 서드파티 바이너리를 다룰 때 유용합니다.

종료 코드를 그대로 쓰기

timeout --preserve-status 30 ./job.sh
echo $?

--preserve-status가 없으면 시간 초과 시 종료 코드가 124로 바뀝니다. 작업 자체의 성공과 실패를 스크립트에서 판정해야 한다면 이 옵션을 붙이고, 시간 초과 여부는 별도로 기록하는 편이 좋아요.

파이프라인 전체에 걸기

timeout은 하나의 명령에만 적용됩니다. 파이프로 연결된 여러 명령을 한꺼번에 제한하려면 셸로 묶어서 실행합니다.

timeout 60 bash -c 'producer | consumer'

이렇게 하면 파이프라인 전체가 하나의 실행 단위로 취급되어 시간 제한이 걸립니다.

상황별 선택 가이드

배포 스크립트에서 원격 서버에 접속하거나 API를 호출할 때는 짧은 시간 제한을 거는 편이 안전합니다. ssh나 curl이 응답 없이 매달리면 배포 파이프라인 전체가 대기 상태로 남기 때문입니다. 이때는 timeout과 클라이언트 자체 옵션을 함께 사용하는 것이 일반적입니다.

데이터 마이그레이션이나 배치 작업처럼 정상적으로 오래 걸리는 작업에는 여유 있는 값을 잡아야 합니다. 너무 짧게 잡으면 작업이 중간에 끊겨 데이터가 어중간한 상태로 남을 수 있으므로, 예상 소요 시간을 먼저 측정한 뒤 그보다 넉넉하게 설정하는 편이 좋아요.

cron으로 도는 정기 작업은 이전 실행이 끝나지 않았을 때 겹치는 문제가 있습니다. timeout으로 상한을 두면 최소한 실행이 무한정 누적되는 상황은 피할 수 있습니다. 다만 겹침 자체를 막으려면 별도의 잠금 장치가 필요합니다.

systemd로 관리하는 서비스라면 유닛 파일의 시간 제한 설정을 사용합니다. TimeoutStartSec과 TimeoutStopSec 같은 지시자를 통해 시작과 종료 단계의 대기 시간을 정할 수 있고, 표기 방식은 systemd.time(7) 문서에 정리되어 있습니다. 셸 스크립트 안에서 timeout을 쓰는 것과 역할이 겹치므로, 어느 계층에서 제한할지 먼저 정하는 것이 좋아요.

컨테이너를 다룰 때는 오케스트레이션 도구가 제공하는 종료 대기 설정과 timeout이 중복되지 않도록 주의해야 합니다. 두 장치가 서로 다른 시점에 신호를 보내면 로그 해석이 어려워집니다.

주의할 점

timeout은 명령과 그 자식 프로세스에 신호를 보내지만, 프로그램이 새 세션을 만들거나 프로세스 그룹을 벗어나면 정리가 되지 않을 수 있습니다. 백그라운드로 분리되는 데몬 형태의 프로그램을 감쌀 때는 특히 주의해야 합니다.

TERM을 무시하도록 작성된 프로그램은 -k 없이는 종료되지 않습니다. 운영 환경에서 정리되지 않은 프로세스가 남으면 포트 충돌이나 파일 잠금 문제로 이어질 수 있으므로, 강제 종료 시점을 함께 지정하는 습관이 좋아요.

셸 내장 명령에는 timeout을 직접 적용할 수 없습니다. cd나 export 같은 내장 명령을 감싸야 한다면 bash -c로 별도 셸을 띄워 실행해야 합니다.

종료 코드 124는 "시간 초과"라는 의미를 가집니다. 실제 명령이 실패해서 나온 코드와 구분되지 않으면 장애 원인을 잘못 짚을 수 있으므로, 로그에 시간 초과 여부를 함께 남기는 편이 안전합니다.

--foreground는 TTY 입력이 필요한 대화형 프로그램을 감쌀 때 씁니다. 이 옵션을 쓰면 timeout이 별도 프로세스 그룹을 만들지 않아, 프로그램이 터미널을 직접 제어할 수 있습니다.

자주 묻는 질문

시간 초과로 종료됐는지 어떻게 확인하나요?

시간 초과가 발생하면 timeout은 종료 코드 124를 반환합니다. 스크립트에서 $? 값을 확인해 124인지 비교하면 시간 초과 여부를 판별할 수 있어요. --preserve-status를 사용한 경우에는 이 구분이 사라지므로, 필요하면 실행 전후로 시각을 기록해 두는 편이 좋습니다.

TERM을 무시하는 프로세스는 어떻게 정리하나요?

-k 옵션으로 강제 종료 시점을 예약하면 됩니다. 먼저 TERM을 보내고, 지정한 시간이 지나도 프로세스가 남아 있으면 KILL을 보냅니다. KILL은 프로세스가 정리 작업을 수행할 기회를 주지 않으므로, 임시 파일이나 잠금 해제가 필요한 프로그램이라면 첫 신호는 TERM으로 두는 것이 좋아요.

파이프라인 전체에 시간 제한을 걸 수 있나요?

timeout은 인자로 받은 하나의 명령에만 적용됩니다. 여러 명령을 파이프로 연결했다면 bash -c로 묶어 하나의 실행 단위로 만들어야 전체에 제한이 걸립니다. 이때 셸 옵션에 따라 파이프의 마지막 명령 종료 코드가 반환된다는 점도 함께 고려해야 합니다.

systemd 서비스에도 같은 개념을 적용할 수 있나요?

유닛 파일의 TimeoutStartSec과 TimeoutStopSec 지시자로 시작과 종료 단계의 대기 시간을 지정할 수 있습니다. 표기 방식은 systemd.time(7) 문서를 따르며, 셸 스크립트에서 timeout을 쓰는 대신 서비스 계층에서 관리할 수 있어요. 두 방식을 동시에 쓰면 어느 쪽이 먼저 동작했는지 로그에서 헷갈릴 수 있습니다.

출처

배포 스크립트에서 프로세스가 남아 포트가 잠기는 문제를 다룬다면 리눅스 프로세스와 시그널 정리 글을 함께 읽어 보시면 좋아요. 신호 종류와 프로세스 그룹 동작을 이해하면 timeout을 어디에 걸어야 하는지 판단하기가 한결 쉬워집니다.