문자열을 찾을 때까지 파일 모니터링


60

tail -f를 사용하여 현재 기록중인 로그 파일을 모니터링하고 있습니다. 특정 문자열이 로그 파일에 기록되면 모니터링을 종료하고 나머지 스크립트를 계속하고 싶습니다.

현재 나는 사용하고 있습니다 :

tail -f logfile.log | grep -m 1 "Server Started"

문자열이 발견되면 grep이 예상대로 종료되지만 스크립트를 계속 사용할 수 있도록 tail 명령도 종료하는 방법을 찾아야합니다.


원래 포스터가 어떤 운영 체제에서 실행되고 있는지 궁금합니다. Linux RHEL5 시스템에서 grep 명령이 일치하고 종료되면 tail 명령이 단순히 죽는다는 사실에 놀랐습니다.
ZaSter

4
@ZaSter : tail다음 줄에서만 죽습니다. 이것을 시도한 date > log; tail -f log | grep -m 1 trigger다음 다른 쉘에서 시도 하면 첫 번째 쉘에서 echo trigger >> log출력 trigger을 볼 수 있지만 명령이 종료되지는 않습니다. 그런 다음 date >> log두 번째 쉘에서 다음을 시도하십시오. 첫 번째 쉘의 명령이 종료됩니다. 그러나 때때로 이것은 너무 늦습니다. 트리거 라인 뒤의 라인이 완료 될 때가 아니라 트리거 라인이 나타난 즉시 종료하려고합니다.
Alfe

@Alfe는 훌륭한 설명과 예입니다.
ZaSter

1
우아한 한 줄 강력한 솔루션입니다 사용 tail+ grep -q00prometheus의 대답처럼
트레버 보이드 스미스

답변:


41

간단한 POSIX 원 라이너

간단한 원 라이너가 있습니다. bash 특정 또는 비 POSIX 트릭이나 명명 된 파이프가 필요하지 않습니다. 정말로 필요한 것은의 종료를 분리하는 tailgrep입니다. 이렇게 grep하면 tail아직 끝나지 않았 더라도 스크립트가 끝날 수 있습니다 . 따라서이 간단한 방법으로 당신을 얻을 수 있습니다 :

( tail -f -n0 logfile.log & ) | grep -q "Server Started"

grep문자열을 찾을 때까지 차단하면 종료됩니다. 함으로써 tail이 독립적으로 실행되도록 그것의 자신의 서브 쉘에서 실행, 우리는 배경에 배치 할 수 있습니다. 한편, 메인 쉘은 grep종료 되 자마자 스크립트 실행을 계속할 수 있습니다. tail다음 줄이 로그 파일에 기록 될 때까지 하위 셸에 머무른 다음 종료합니다 (주 스크립트가 종료 된 후에도). 요점은 파이프 라인이 더 이상 tail종료를 기다리지 않으므로 파이프 라인이 종료하자마자 종료된다는 것 grep입니다.

약간의 조정 :

  • tail문자열이 로그 파일의 이전에 존재하는 경우 -n0 옵션을 사용하면 현재 마지막 로그 파일 행에서 읽기를 시작합니다.
  • tail-f 대신 -F 를 제공 할 수 있습니다 . POSIX는 아니지만 tail대기 중 로그가 회전하더라도 작동 할 수 있습니다 .
  • -m1이 아닌 -q 옵션 grep은 처음 발생한 후에 종료하지만 트리거 라인을 인쇄하지 않습니다. 또한 POSIX이며 -m1이 아닙니다.

3
이 접근 방식은 tail백그라운드에서 계속 실행됩니다. tail백그라운드 서브 쉘 내 에서 PID 를 캡처 하여 메인 쉘을 노출 시키려면 어떻게해야합니까? 를 tail사용하여 모든 세션 첨부 프로세스를 종료하여 하위 선택적 해결 방법 만 제시 할 수 있습니다 pkill -s 0 tail.
Rick van der Zwet

1
대부분의 사용 사례에서는 문제가되지 않습니다. 처음에이 작업을 수행하는 이유는 더 많은 행이 로그 파일에 기록 될 것으로 예상하기 때문입니다. tail깨진 파이프에 쓰려고하자마자 종료됩니다. 파이프 grep는 완료 되 자마자 끊어 지므로 grep완료되면 tail로그 파일에 줄이 하나 더 있으면 종료됩니다.
00prometheus

내가이 솔루션을 사용하면 내가 그랬어 하지 추진 배경 tail -f.
Trevor Boyd Smith

2
@Trevor Boyd Smith, 그렇습니다. 대부분의 상황에서 작동하지만 OP 문제는 꼬리가 종료 될 때까지 grep이 완료되지 않고 grep이 종료 된 로그 파일에 다른 줄이 나타날 때까지 꼬리가 종료되지 않는다는 것입니다 (꼬리 시도시) grep 결말에 의해 파손 된 파이프 라인에 공급). 따라서 백그라운드 테일을 지정하지 않으면 grep이 포착하는 행이 아니라 추가 파일이 로그 파일에 나타날 때까지 스크립트가 계속 실행되지 않습니다.
00prometheus

"[필수 문자열 패턴] 다음에 다른 줄이 나타날 때까지 트레일이 종료되지 않습니다.": 매우 미묘하며 완전히 빠졌습니다. 내가 찾던 패턴이 중간에 있었고 모두 빨리 인쇄 되었기 때문에 나는 알지 못했습니다. (당신이 묘사하는 행동은 매우 미묘합니다)
Trevor Boyd Smith

59

허용 된 답변이 저에게 효과적이지 않으며 혼란스럽고 로그 파일을 변경합니다.

나는 이와 같은 것을 사용하고있다 :

tail -f logfile.log | while read LOGLINE
do
   [[ "${LOGLINE}" == *"Server Started"* ]] && pkill -P $$ tail
done

로그 라인이 패턴과 일치하면 tail이 스크립트로 시작을 종료하십시오.

참고 : 화면에서 출력을 보려는 | tee /dev/tty경우 while 루프에서 테스트하기 전에 라인을 에코하거나 에코하십시오.


2
이것은 작동하지만 pkillPOSIX에서 지정하지 않았으며 모든 곳에서 사용할 수는 없습니다.
Richard Hansen

2
while 루프가 필요하지 않습니다. watch와 함께 -g 옵션을 사용하면 불쾌한 pkill 명령을 절약 할 수 있습니다.
l1zard

@ l1zard 당신은 그것을 육체로 할 수 있습니까? 특정 줄이 나타날 때까지 로그 파일의 꼬리를 어떻게 보셨습니까? (더 중요하지는 않지만 watch -g가 추가되었을 때 궁금합니다. 해당 옵션이있는 최신 데비안 서버와이 옵션이없는 다른 오래된 RHEL 기반 서버가 있습니다.)
Rob Whelan

왜 꼬리가 필요한지 분명하지 않습니다. 내가 올바르게 이해하는 한 사용자는 로그 파일의 특정 키워드가 나타날 때 특정 명령을 실행하려고합니다. watch를 사용하여 아래에 주어진 명령은 바로이 작업을 수행합니다.
l1zard

확실하지 않습니다-주어진 문자열이 로그 파일에 추가 될 때 확인 중 입니다. Tomcat 또는 JBoss가 완전히 시작된 시점을 확인하는 데 사용합니다. 그들은 발생할 때마다 "서버 시작"(또는 유사)을 작성합니다.
Rob Whelan

16

Bash를 사용하는 경우 (적어도 POSIX에 의해 정의되지 않은 것으로 보이므로 일부 쉘에서는 누락되었을 수 있음) 구문을 사용할 수 있습니다

grep -m 1 "Server Started" <(tail -f logfile.log)

이미 언급 한 FIFO 솔루션과 매우 유사하지만 작성하기가 훨씬 간단합니다.


1
이것은 작동하지만, 꼬리는 여전히 보내 당신 전까지 실행 SIGTERM합니다 (Ctrl + C, exit 명령을하거나 죽일)
MEMS

3
@mems, 로그 파일의 추가 라인이 수행합니다. 이 파일 tail을 읽고 출력을 시도한 다음 종료하는 SIGPIPE를 수신합니다. 따라서 원칙적으로 옳습니다. 는 tail아무것도 다시 로그 파일에 기록되지됩니다 경우 무기한 실행될 수 있습니다. 실제로 이것은 많은 사람들에게 매우 깔끔한 솔루션 일 수 있습니다.
Alfe

14

tail종료 하는 몇 가지 방법이 있습니다 .

불쌍한 접근 방식 : tail다른 줄을 작성하도록 강요

일치하는 것을 발견하고 종료 한 tail직후 다른 출력 줄을 강제 로 쓸 수 있습니다 grep. 이 원인이됩니다 tail를 얻을 SIGPIPE이 종료 원인. 이를 수행하는 한 가지 방법은 종료 tail후 모니터링중인 파일을 수정하는 것 grep입니다.

예제 코드는 다음과 같습니다.

tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }

이 예제에서는 stdout을 닫을 cat때까지 종료 되지 않으므로 stdin을 닫을 기회가 있기 전에 파이프에 쓸 수 없습니다 . 수정되지 않은 표준 출력을 전파하는 데 사용됩니다 .greptailgrepcatgrep

이 방법은 비교적 간단하지만 몇 가지 단점이 있습니다.

  • 경우 grep닫히고는 표준 입력을 닫기 전에 표준 출력, 항상 경쟁 조건이있을 것이다 : grep트리거, 표준 출력 종료 cat트리거, 종료 echo트리거 tail출력 라인을. 이 줄이 grep이전에 전송되어 grepstdin을 닫을 기회가 있었으면 다른 줄을 쓸 때까지를 tail얻지 못합니다 SIGPIPE.
  • 로그 파일에 대한 쓰기 액세스 권한이 필요합니다.
  • 로그 파일을 수정하여 확인해야합니다.
  • 다른 프로세스와 동시에 쓰면 로그 파일이 손상 될 수 있습니다 (쓰기가 인터리브되어 줄 바꿈이 로그 메시지 중간에 표시 될 수 있음).
  • 이 접근 방식은 tail다른 프로그램에서는 작동하지 않습니다.
  • 세 번째 파이프 라인 단계에서는 bashPIPESTATUS배열 과 같은 POSIX 확장을 사용하지 않는 한 두 번째 파이프 라인 단계의 리턴 코드에 액세스하기가 어렵습니다 . 이 경우 grep에는 항상 0을 반환 하기 때문에 큰 문제는 아니지만 일반적으로 중간 단계는 반환 코드에 관심이있는 다른 명령 (예 : "서버 시작"이 감지 될 때 0을 반환하는 명령)으로 대체 될 수 있습니다. 1 "서버 시작 실패"가 감지 된 경우).

다음 접근 방식은 이러한 제한을 피합니다.

더 나은 접근 방식 : 파이프 라인을 피하십시오

FIFO를 사용하여 파이프 라인을 완전히 피할 수 있으므로 한 번 grep리턴 하면 실행을 계속할 수 있습니다. 예를 들면 다음과 같습니다.

fifo=/tmp/tmpfifo.$$
mkfifo "${fifo}" || exit 1
tail -f logfile.log >${fifo} &
tailpid=$! # optional
grep -m 1 "Server Started" "${fifo}"
kill "${tailpid}" # optional
rm "${fifo}"

주석이 표시된 줄을 # optional제거해도 프로그램은 계속 작동합니다. tail다른 입력 줄을 읽거나 다른 프로세스에 의해 종료 될 때까지 남아 있습니다.

이 방법의 장점은 다음과 같습니다.

  • 로그 파일을 수정할 필요가 없습니다
  • 이 접근법은 다른 유틸리티 외에도 작동합니다. tail
  • 경쟁 조건으로 고통받지 않습니다.
  • 당신은 쉽게 리턴 값을 얻을 수 있습니다 grep(또는 사용중인 대체 명령)

이 방법의 단점은 복잡성, 특히 FIFO 관리입니다. 임시 파일 이름을 안전하게 생성해야하며 사용자가 중간에 Ctrl-C를 누르더라도 임시 FIFO가 삭제되도록해야합니다. 스크립트. 트랩을 사용하여 수행 할 수 있습니다.

대체 접근 방식 : 메시지 보내기 tail

다음 tail과 같은 신호를 보내 파이프 라인 단계를 종료 할 수 있습니다 SIGTERM. 문제는 코드에서 동일한 위치에있는 tailPID와 grep종료 여부 를 확실하게 알고 있습니다.

같은 파이프 라인을 사용하면 배경 및 읽기 를 통해 변수에 PID tail -f ... | grep ...를 저장하기 위해 첫 번째 파이프 라인 단계를 쉽게 수정할 수 있습니다. 그것은 실행하는 두 번째 파이프 라인 단계 수정도 쉽게 때 종료. 문제는 파이프 라인의 두 단계가 별도의 "실행 환경"(POSIX 표준의 용어로)에서 실행되므로 두 번째 파이프 라인 단계는 첫 번째 파이프 라인 단계에서 설정 한 변수를 읽을 수 없다는 것입니다. 쉘 변수를 사용하지 않으면 두 번째 단계는 PID를 계산하여 반환 할 때 죽일 수 있거나 첫 번째 단계에서 반환 할 때 통보해야 합니다.tailtail$!killgreptailtailgrepgrep

두 번째 단계는 의 PID pgrep를 얻는 데 사용할 수 tail있지만 신뢰할 수 없으며 (잘못된 프로세스와 일치 할 수 있음) 이식 할 수 없습니다 ( pgrepPOSIX 표준으로 지정되지 않음).

첫 번째 단계는 PID를 연결하여 파이프를 통해 PID를 두 번째 단계로 보낼 수 echo있지만이 문자열은 tail의 출력 과 혼합됩니다 . 이 둘을 역 다중화하려면의 출력에 따라 복잡한 이스케이프 체계가 필요할 수 있습니다 tail.

FIFO를 사용하여 종료시 두 번째 파이프 라인 단계가 첫 번째 파이프 라인 단계에 알리도록 할 수 있습니다 grep. 그러면 첫 번째 단계는 죽일 수 있습니다 tail. 예제 코드는 다음과 같습니다.

fifo=/tmp/notifyfifo.$$
mkfifo "${fifo}" || exit 1
{
    # run tail in the background so that the shell can
    # kill tail when notified that grep has exited
    tail -f logfile.log &
    # remember tail's PID
    tailpid=$!
    # wait for notification that grep has exited
    read foo <${fifo}
    # grep has exited, time to go
    kill "${tailpid}"
} | {
    grep -m 1 "Server Started"
    # notify the first pipeline stage that grep is done
    echo >${fifo}
}
# clean up
rm "${fifo}"

이 방법에는 이전 방법의 장단점이 있지만 더 복잡합니다.

버퍼링에 대한 경고

POSIX를 사용하면 stdin 및 stdout 스트림을 완전히 버퍼링 할 수 있습니다. 즉 , 임의의 시간 동안 tail출력이 처리되지 않을 수 있습니다 grep. GNU 시스템에는 아무런 문제가 없어야합니다. GNU grepread()모든 버퍼링을 피하는 GNU를 사용 하며 GNU tail -ffflush()stdout에 쓸 때 정기적으로 호출합니다 . 비 GNU 시스템은 버퍼를 비활성화하거나 정기적으로 플러시하기 위해 특별한 작업을 수행해야 할 수도 있습니다.


당신의 솔루션 (다른 사람들처럼, 나는 당신을 비난하지 않을 것입니다)은 모니터링이 시작되기 전에 이미 로그 파일에 기록 된 것을 놓칠 것입니다. tail -f에만 출력의 마지막 10 줄을 한 다음 모든 다음. 이를 개선하기 위해 -n 10000꼬리에 옵션 을 추가 하여 마지막 10000 줄도 제공 할 수 있습니다.
Alfe

또 다른 아이디어 : 귀하의 fifo 솔루션은 tail -ffifo를 통해 출력을 전달 하고 grepping하여 직선화 할 수 있다고 생각 합니다 mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f.
Alfe

@Alfe : 틀릴 수도 있지만 tail -f log, FIFO에 쓰기를하면 일부 시스템 (예 : GNU / Linux)이 라인 기반 버퍼링 대신 블록 기반 버퍼링을 사용하게 grep되므로 일치하는 라인이 보이지 않을 수 있습니다 로그에 나타납니다. 시스템은 stdbufGNU coreutils 와 같은 버퍼링을 변경하는 유틸리티를 제공 할 수 있습니다 . 그러나 이러한 유틸리티는 이식 할 수 없습니다.
Richard Hansen

1
@Alfe : 실제로 POSIX는 터미널과 상호 작용할 때를 제외하고 버퍼링에 대해 아무 말도하지 않는 것처럼 보입니다. 표준 관점에서 보면 간단한 솔루션이 내 복잡한 솔루션만큼 우수하다고 생각합니다. 그러나 각 경우에 다양한 구현이 실제로 어떻게 작동하는지 100 % 확신하지 못합니다.
Richard Hansen

실제로, 나는 이제 grep -q -m 1 trigger <(tail -f log)다른 곳에서 제안 된 것보다 훨씬 더 간단하게 tail진행하고 배경보다 한 줄 더 길게 달리는 것이 필요 하다는 사실을 알고 살아 갑니다.
Alfe

9

@ 00prometheus 답변을 확장하겠습니다 (최고의 답변입니다).

무한정 기다리는 대신 타임 아웃을 사용해야 할 수도 있습니다.

아래의 bash 함수는 주어진 검색어가 나타나거나 주어진 시간이 초과 될 때까지 차단됩니다.

문자열이 시간 초과 내에 발견되면 종료 상태는 0이됩니다.

wait_str() {
  local file="$1"; shift
  local search_term="$1"; shift
  local wait_time="${1:-5m}"; shift # 5 minutes as default timeout

  (timeout $wait_time tail -F -n0 "$file" &) | grep -q "$search_term" && return 0

  echo "Timeout of $wait_time reached. Unable to find '$search_term' in '$file'"
  return 1
}

서버를 시작한 직후에 로그 파일이 존재하지 않을 수 있습니다. 이 경우 문자열을 검색하기 전에 표시 될 때까지 기다려야합니다.

wait_server() {
  echo "Waiting for server..."
  local server_log="$1"; shift
  local wait_time="$1"; shift

  wait_file "$server_log" 10 || { echo "Server log file missing: '$server_log'"; return 1; }

  wait_str "$server_log" "Server Started" "$wait_time"
}

wait_file() {
  local file="$1"; shift
  local wait_seconds="${1:-10}"; shift # 10 seconds as default timeout

  until test $((wait_seconds--)) -eq 0 -o -f "$file" ; do sleep 1; done

  ((++wait_seconds))
}

사용 방법은 다음과 같습니다.

wait_server "/var/log/server.log" 5m && \
echo -e "\n-------------------------- Server READY --------------------------\n"

그렇다면 timeout명령 은 어디에 있습니까?
ayanamist

실제로는 사용할 timeout수없고 이미 종료 된 서버를 무기한으로 기다리지 않는 유일한 안정적인 방법입니다.
gluk47

1
이 답변이 최고입니다. 그냥 복사 기능을하고 전화, 그것은 매우 쉽고 재사용의
흐리 스토 Vrigazov

6

그래서 몇 가지 테스트를 한 후이 작업을 수행하는 빠른 한 줄 방법을 찾았습니다. grep이 종료되면 tail -f가 종료되지만 캐치가 있습니다. 파일을 열고 닫은 경우에만 트리거되는 것처럼 보입니다. grep이 일치하는 것을 찾으면 파일에 빈 문자열을 추가 하여이 작업을 수행했습니다.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> logfile \;

파일의 열기 / 닫기가 파이프를 닫았다는 것을 깨닫기 위해 꼬리를 트리거하는 이유를 잘 모르겠 으므로이 동작에 의존하지 않습니다. 하지만 지금은 효과가있는 것 같습니다.

닫히는 이유는 -F 플래그와 -f 플래그를 비교하십시오.


1
이것은 로그 파일에 추가하면 tail다른 행을 출력하지만 그때까지 grep종료되었을 수 있기 때문에 작동합니다 (아마 경쟁 조건이 있음). 경우 grep시간에 의해 종료 된 것은 tail또 다른 선을 기록, tail을 얻을 것이다 SIGPIPE. 그 tail즉시 종료됩니다.
Richard Hansen

1
이 방법의 단점 : (1) 경쟁 조건이 있습니다 (항상 즉시 종료되지는 않음). (2) 로그 파일에 대한 쓰기 액세스가 필요합니다. (3) 로그 파일을 수정하여 확인해야합니다. 로그 파일 (5) tail(6) 에만 작동 합니다. 리턴 코드를 쉽게 얻을 수 없기 때문에 다른 문자열 일치 ( "서버 시작"대 "서버 시작 실패")에 따라 다르게 동작하도록 쉽게 조정할 수 없습니다. 파이프 라인의 중간 단계. 이러한 모든 문제를 피할 수있는 대안이 있습니다. 제 답변을 참조하십시오.
Richard Hansen

6

현재 주어진대로 tail -f여기 에있는 모든 솔루션은 이전에 기록 된 "서버 시작"줄을 선택할 위험이 있습니다 (이는 기록 된 줄 수 및 로그 파일 회전 /에 따라 특정 경우에 문제가 될 수도 있고 그렇지 않을 수도 있음). 잘림).

bmike 가 perl snippit로 보여준 tail것처럼 지나치게 복잡한 것보다는 더 똑똑한 것을 사용하십시오 . 가장 간단한 솔루션은 시작중지 조건 패턴 과 retail함께 정규식 지원을 통합 한 솔루션입니다 .

retail -f -u "Server Started" server.log > /dev/null

이것은 해당 문자열 tail -f의 첫 번째 인스턴스가 나타날 때까지 파일을 정상적으로 따르고 종료합니다. (이 -u옵션은 일반 "팔로우"모드 일 때 파일의 마지막 10 줄에있는 기존 줄에서는 트리거되지 않습니다.)


coreutilstail 에서 GNU를 사용하는 경우 다음으로 가장 간단한 옵션은 FIFO (named pipe)를 사용하는 것입니다.--pid

mkfifo ${FIFO:=serverlog.fifo.$$}
grep -q -m 1 "Server Started" ${FIFO}  &
tail -n 0 -f server.log  --pid $! >> ${FIFO}
rm ${FIFO}

FIFO는 PID를 획득하고 전달하기 위해 프로세스를 별도로 시작해야하기 때문에 사용됩니다. FIFO은 아직 원인이 적시에 쓰기 위해 주위를 어슬렁 같은 문제로 고통 tail받을 SIGPIPE를 , 사용 --pid하도록 옵션 tail이 그 통지 할 때 종료가 grep종료되었습니다 (통상적으로 모니터링하는 데 사용 작가 오히려보다 처리 독자를 하지만, tail'아무튼 정말 걱정). 기존 줄이 일치를 유발하지 않도록 옵션 -n 0과 함께 사용됩니다 tail.


마지막으로 stateful tail을 사용할 수 있습니다. 현재 파일 오프셋이 저장되므로 후속 호출에서는 새 줄만 표시됩니다 (파일 회전도 처리 함). 이 예는 이전 FWTK retail* 를 사용합니다 .

retail "${LOGFILE:=server.log}" > /dev/null   # skip over current content
while true; do
    [ "${LOGFILE}" -nt ".${LOGFILE}.off" ] && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
    sleep 2
done

* 이전 옵션과 동일한 이름, 다른 프로그램입니다.

CPU- 호깅 루프가 아닌 파일의 타임 스탬프를 상태 파일 ( .${LOGFILE}.off)과 비교하고 휴면 상태를 유지하십시오. -T필요한 경우 " "를 사용하여 상태 파일의 위치를 ​​지정하십시오. 위는 현재 디렉토리를 가정합니다. 해당 조건을 건너 뛰거나 Linux에서 inotifywait대신 보다 효율적으로 사용할 수 있습니다 .

retail "${LOGFILE:=server.log}" > /dev/null
while true; do
    inotifywait -qq "${LOGFILE}" && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
done

retail"120 초가 지났는데도 소매점에서 여전히 라인을 읽지 못하면 오류 코드를 제공하고 소매점을 종료하십시오"와 같은 시간 종료와 결합 할 수 있습니까 ?
kiltek

@kiltek은 GNU timeout(coreutils)를 사용 retail하여 시간 초과시 종료 코드 124 를 시작 하고 확인합니다 ( timeout설정 한 시간 이후에 시작하기 위해 사용하는 명령이 모두 종료 됨)
mr.spuratic

4

프로세스 제어 및 신호 처리에 들어가야하므로 약간 까다로울 수 있습니다. 더 많은 kludgey는 PID 추적을 사용하는 두 가지 스크립트 솔루션입니다. 이와 같은 명명 된 파이프를 사용하는 것이 좋습니다.

어떤 쉘 스크립트를 사용하고 있습니까?

빠르고 더러운 하나의 스크립트 솔루션-File : Tail을 사용하여 perl 스크립트를 작성합니다 .

use File::Tail;
$file=File::Tail->new(name=>$name, maxinterval=>300, adjustafter=>7);
while (defined($line=$file->read)) {
    last if $line =~ /Server started/;
}

따라서 while 루프 내부를 인쇄하는 대신 문자열 일치를 필터링하고 while 루프를 중단하여 스크립트를 계속 진행할 수 있습니다.

이 중 하나는 찾고있는 감시 흐름 제어를 구현하기 위해 약간의 학습이 필요합니다.


bash를 사용합니다. 내 perl-fu는 그렇게 강하지는 않지만, 이것에 대해 설명하겠습니다.
Alex Hofsteede

파이프를 사용하십시오-그들은 bash를 좋아하고 bash는 그들을 좋아합니다. (그리고 파이프 중 하나에 충돌하면 백업 소프트웨어가 당신을 존중할 것입니다)
bmike

maxinterval=>3005 분마다 파일을 확인한다는 의미입니다. 내 줄이 잠시 파일에 나타날 것임을 알고 있으므로 훨씬 더 적극적인 폴링을 사용하고 있습니다.maxinterval=>0.2, adjustafter=>10000
Stephen Ostermiller

2

파일이 나타날 때까지 기다리십시오

while [ ! -f /path/to/the.file ] 
do sleep 2; done

파일에서 문자열이 나타날 때까지 기다립니다.

while ! grep "the line you're searching for" /path/to/the.file  
do sleep 10; done

https://superuser.com/a/743693/129669


2
이 폴링에는 두 가지 주요 단점이 있습니다. 1. 로그를 반복해서 반복하여 계산 시간을 낭비합니다. /path/to/the.file1.4GB가 큰 것을 고려하십시오 . 이것이 문제라는 것이 분명합니다. 2. 로그 항목이 나타날 때 필요 이상으로 대기합니다 (최악의 경우 10 초).
Alfe

2

이 솔루션보다 깨끗한 솔루션을 상상할 수 없습니다.

#!/usr/bin/env bash
# file : untail.sh
# usage: untail.sh logfile.log "Server Started"
(echo $BASHPID; tail -f $1) | while read LINE ; do
    if [ -z $TPID ]; then
        TPID=$LINE # the first line is used to store the previous subshell PID
    else
        echo "$LINE"; [[ "$LINE" == *"${*:2}"* ]] && kill -3 $TPID && break
    fi
done

좋아, 아마도 이름이 개선 될 수 있습니다 ...

장점 :

  • 특별한 유틸리티를 사용하지 않습니다
  • 디스크에 쓰지 않습니다
  • 우아하게 꼬리를 끝내고 파이프를 닫습니다.
  • 꽤 짧고 이해하기 쉽습니다.

2

그렇게하기 위해 꼬리가 필요하지 않습니다. 나는 watch 명령이 당신이 찾고있는 것이라고 생각합니다 . watch 명령은 파일의 출력을 모니터하고 출력이 변경되면 -g 옵션 으로 종료 될 수 있습니다 .

watch -g grep -m 1 "Server Started" logfile.log && Yournextaction

1
2 초마다 한 번씩 실행되므로 로그 파일에 줄이 나타나면 즉시 종료되지 않습니다. 또한 로그 파일이 매우 크면 제대로 작동하지 않습니다.
Richard Hansen


1

알렉스 나는 이것이 당신을 많이 도울 것이라고 생각합니다.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> /dev/null ;

이 명령은 로그 파일에 항목을 제공하지 않지만 자동으로 grep합니다 ...


1
이것은 작동하지 않습니다. logfile그렇지 않으면 추가 해야 tail합니다. 다른 라인을 출력하고 grep(via를 통해 SIGPIPE) 죽은 것을 감지 하기까지는 시간이 오래 걸릴 수 있습니다 .
Richard Hansen

1

다음은 로그 파일에 쓸 필요가없는 훨씬 더 나은 솔루션입니다. 일부 경우에는 매우 위험하거나 불가능합니다.

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

현재는 부작용이 하나 뿐이며 tail다음 줄이 로그에 기록 될 때까지 프로세스는 백그라운드로 유지됩니다.


tail -n +0 -f파일의 처음부터 시작합니다. tail -n 0 -f파일의 끝에서 시작합니다.
Stephen Ostermiller

1
내가 얻는 또 다른 부작용 :myscript.sh: line 14: 7845 Terminated sh -c 'tail...
Stephen Ostermiller

이 답변에서 "다음 목록"은 "다음 줄"이어야한다고 생각합니다.
Stephen Ostermiller

이것은 작동하지만 tail프로세스는 백그라운드에서 계속 실행됩니다.
cbaldan

1

다른 솔루션에는 몇 가지 문제가 있습니다.

  • 로깅 프로세스가 이미 다운되었거나 루프 중에 다운되면 무한정 실행됩니다.
  • 보아야 만하는 로그 편집
  • 불필요하게 추가 파일 작성
  • 추가 로직을 허용하지 않음

다음은 Tomcat을 예로 사용하여 얻은 것입니다 (시작하는 동안 로그를 보려면 해시를 제거하십시오).

function startTomcat {
    loggingProcessStartCommand="${CATALINA_HOME}/bin/startup.sh"
    loggingProcessOwner="root"
    loggingProcessCommandLinePattern="${JAVA_HOME}"
    logSearchString="org.apache.catalina.startup.Catalina.start Server startup"
    logFile="${CATALINA_BASE}/log/catalina.out"

    lineNumber="$(( $(wc -l "${logFile}" | awk '{print $1}') + 1 ))"
    ${loggingProcessStartCommand}
    while [[ -z "$(sed -n "${lineNumber}p" "${logFile}" | grep "${logSearchString}")" ]]; do
        [[ -z "$(ps -ef | grep "^${loggingProcessOwner} .* ${loggingProcessCommandLinePattern}" | grep -v grep)" ]] && { echo "[ERROR] Tomcat failed to start"; return 1; }
        [[ $(wc -l "${logFile}" | awk '{print $1}') -lt ${lineNumber} ]] && continue
        #sed -n "${lineNumber}p" "${logFile}"
        let lineNumber++
    done
    #sed -n "${lineNumber}p" "${logFile}"
    echo "[INFO] Tomcat has started"
}

1

tail명령은 백그라운드로하고 PID는에 에코 할 수있다 grep서브 쉘. 에서 grep서브 쉘 EXIT에 대한 트랩 처리기는 죽일 수있는 tail명령을 사용합니다.

( (sleep 1; exec tail -f logfile.log) & echo $! ; wait ) | 
     (trap 'kill "$pid"' EXIT; pid="$(head -1)"; grep -m 1 "Server Started")

1

그들 모두를 읽으십시오. tldr : grep에서 tail 종료를 분리합니다.

가장 편리한 두 가지 형태는

( tail -f logfile.log & ) | grep -q "Server Started"

그리고 배쉬가 있다면

grep -m 1 "Server Started" <(tail -f logfile.log)

그러나 배경에 앉아있는 꼬리가 당신을 귀찮게한다면, fifo 나 다른 대답보다 더 좋은 방법이 있습니다. 배쉬가 필요합니다.

coproc grep -m 1 "Server Started"
tail -F /tmp/x --pid $COPROC_PID >&${COPROC[1]}

또는 출력되는 것이 꼬리가 아닌 경우

coproc command that outputs
grep -m 1 "Sever Started" ${COPROC[0]}
kill $COPROC_PID

0

inotify를 사용하십시오 (inotifywait)

파일 변경을 위해 inotifywait를 설정 한 다음 발견되지 않은 경우 grep로 파일을 확인하십시오. 루프를 종료하면 inotifywait를 다시 실행하십시오.


이런 식으로 파일을 작성할 때마다 전체 파일을 다시 확인해야합니다. 로그 파일에는 제대로 작동하지 않습니다.
grawity

1
다른 방법은 두 개의 스크립트를 만드는 것입니다. 1. tail -f logfile.log | grep -m 1 "서버 시작"> / tmp / found 2. firstscript.sh & MYPID = $!; inotifywait -e 수정 / tmp / found; kill -KILL-$ MYPID
Evengard

PID를 캡처 한 다음 inotifywait를 사용하여 응답을 편집하여 grep에 익숙하지만보다 정교한 도구가 필요한 사람을 쉽게 파악할 수있는 우아한 솔루션을 사용하고 싶습니다.
bmike

캡처하고 싶은 PID? 당신이 원하는 것을 조금 더 설명하면 노력할 수 있습니다
Evengard

0

줄이 작성 되 자마자 떠나고 싶지만 타임 아웃 후에도 떠나고 싶습니다.

if (timeout 15s tail -F -n0 "stdout.log" &) | grep -q "The string that says the startup is successful" ; then
    echo "Application started with success."
else
    echo "Startup failed."
    tail stderr.log stdout.log
    exit 1
fi

-2

이것은 어떤가요:

사실이지만; 경우에! -z $ (grep "myRegEx"myLog.log)]; 그런 다음 휴식하십시오. fi; 끝난

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.