tail
종료 하는 몇 가지 방법이 있습니다 .
불쌍한 접근 방식 : tail
다른 줄을 작성하도록 강요
일치하는 것을 발견하고 종료 한 tail
직후 다른 출력 줄을 강제 로 쓸 수 있습니다 grep
. 이 원인이됩니다 tail
를 얻을 SIGPIPE
이 종료 원인. 이를 수행하는 한 가지 방법은 종료 tail
후 모니터링중인 파일을 수정하는 것 grep
입니다.
예제 코드는 다음과 같습니다.
tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }
이 예제에서는 stdout을 닫을 cat
때까지 종료 되지 않으므로 stdin을 닫을 기회가 있기 전에 파이프에 쓸 수 없습니다 . 수정되지 않은 표준 출력을 전파하는 데 사용됩니다 .grep
tail
grep
cat
grep
이 방법은 비교적 간단하지만 몇 가지 단점이 있습니다.
- 경우
grep
닫히고는 표준 입력을 닫기 전에 표준 출력, 항상 경쟁 조건이있을 것이다 : grep
트리거, 표준 출력 종료 cat
트리거, 종료 echo
트리거 tail
출력 라인을. 이 줄이 grep
이전에 전송되어 grep
stdin을 닫을 기회가 있었으면 다른 줄을 쓸 때까지를 tail
얻지 못합니다 SIGPIPE
.
- 로그 파일에 대한 쓰기 액세스 권한이 필요합니다.
- 로그 파일을 수정하여 확인해야합니다.
- 다른 프로세스와 동시에 쓰면 로그 파일이 손상 될 수 있습니다 (쓰기가 인터리브되어 줄 바꿈이 로그 메시지 중간에 표시 될 수 있음).
- 이 접근 방식은
tail
다른 프로그램에서는 작동하지 않습니다.
- 세 번째 파이프 라인 단계에서는
bash
의 PIPESTATUS
배열 과 같은 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
. 문제는 코드에서 동일한 위치에있는 tail
PID와 grep
종료 여부 를 확실하게 알고 있습니다.
같은 파이프 라인을 사용하면 배경 및 읽기 를 통해 변수에 PID tail -f ... | grep ...
를 저장하기 위해 첫 번째 파이프 라인 단계를 쉽게 수정할 수 있습니다. 그것은 실행하는 두 번째 파이프 라인 단계 수정도 쉽게 때 종료. 문제는 파이프 라인의 두 단계가 별도의 "실행 환경"(POSIX 표준의 용어로)에서 실행되므로 두 번째 파이프 라인 단계는 첫 번째 파이프 라인 단계에서 설정 한 변수를 읽을 수 없다는 것입니다. 쉘 변수를 사용하지 않으면 두 번째 단계는 PID를 계산하여 반환 할 때 죽일 수 있거나 첫 번째 단계에서 반환 할 때 통보해야 합니다.tail
tail
$!
kill
grep
tail
tail
grep
grep
두 번째 단계는 의 PID pgrep
를 얻는 데 사용할 수 tail
있지만 신뢰할 수 없으며 (잘못된 프로세스와 일치 할 수 있음) 이식 할 수 없습니다 ( pgrep
POSIX 표준으로 지정되지 않음).
첫 번째 단계는 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 grep
는 read()
모든 버퍼링을 피하는 GNU를 사용 하며 GNU tail -f
는 fflush()
stdout에 쓸 때 정기적으로 호출합니다 . 비 GNU 시스템은 버퍼를 비활성화하거나 정기적으로 플러시하기 위해 특별한 작업을 수행해야 할 수도 있습니다.