[Linux] tail -f | grep -v | awk 등 파이프로 여러 명령어 조합시 결과값 출력이 느리거나 안나올 때 (get slow or no result with multiple commands and multiple pipes like tail -f | grep -v | awk)
Linux 2020. 1. 3. 16:01급한 문제가 생겨 access.log에서 특정 IP를 제외하고 실시간 접속상황을 분석해야 할 경우가 있다고 가정하자.
아래처럼 명령어를 조합해서 나오는 결과값을 분석하려 할 것이고, 원하는 결과값이 잘 나올 것이다.
[root@localhost]# tail -f /var/log/apache/access.log | grep -v 'MY IP'
그러다가 갑자기 IP를 2개 제외해야 하는 상황이 벌어졌다고 하자.
급한대로 grep -v 를 하나 더 추가해 파이프로 연결한다. (다른 방법도 많지만 여기선 그게 문제가 아니므로 잠시 잊자...)
[root@localhost]# tail -f /var/log/apache/access.log | grep -v 'MY IP' | grep -v 'MY FRIEND IP'
근데 여기서부터 필터링을 거친 로그가 미적미적거리면서 나오거나, 접속이 빈번하지 않은 경우라면 아예 안나오는 등, 결과값이 좀 이상하게 나오기 시작할 것이다.
결론부터 말하자면 원인은 libc를 이용하는 stdio가 출력쪽이 tty가 아닌 경우 효율적인 I/O를 위해 버퍼링을 한다는 것이다. (버퍼 크기는 일반적으로 4096)
tail -f | grep -v | grep -v 로 이루어진 위의 예에서는 첫번째 grep -v 가 두번째 grep -v 에게 넘겨주기 전에 버퍼링을 한다.
이 문제를 해결하기 위해선 여러가지 방법이 있다.
온갖 기상천외한 방법이 많지만, 여기선 2가지만 적겠다.
우선 각 명령어마다 버퍼링을 하지 않도록 설정하는 옵션들이 있다.
tail : -f 가 붙으면 바로 flush
tcpdump : -l
grep : --line-buffered
awk : fflush() 함수사용
sed : -u, --unbuffered
위의 예에서 사용한 명령어를 제대로 돌아가게 하려면 아래처럼 해주면 된다.
[root@localhost\]# tail -f /var/log/apache/access.log | grep --line-buffered -v 'MY IP' | grep -v 'MY FRIEND IP'
하지만 명령어마다 옵션을 외우기가 귀찮다면 GNU coreutils에 포함되어 있는 stdbuf를 사용하면 해결된다.
표준입출력 관련해서 여러가지 옵션이 있지만 stdbuf -oL (line buffered = new line을 만나면 flush) 정도만 기억해둬도 된다.
위의 예에서 사용한 명령어를 stdbuf를 이용해 제대로 돌아가게 하려면 아래처럼 해주면 된다.
[root@localhost]# tail -f /var/log/apache/access.log | stdbuf -oL grep -v 'MY IP' | grep -v 'MY FRIEND IP'
아래는 stdio 버퍼링이 과연 그런지 테스트.
// 0.1초마다 1~3 중 하나의 숫자를 랜덤하게 출력
[root@localhost]# while sleep 0.1; do echo $((1 + RANDOM % 3)); done
1
3
2
1
1
3
1
... ...
// "2" 만 제외. 여기까진 잘됨
[root@localhost]# while sleep 0.1; do echo $((1 + RANDOM % 3)); done | grep -v "2"
3
3
3
1
3
1
1
... ...
// "1" 도 제외하려고 함. 갑자기 아무것도 안나옴.
[root@localhost]# while sleep 0.1; do echo $((1 + RANDOM % 3)); done | grep -v "2" | grep -v "1"
// 위에서 알아본 내용대로 첫번째 grep의 버퍼링 때문인지 확인. 잘나옴.
[root@localhost]# while sleep 0.1; do echo $((1 + RANDOM % 3)); done | grep --line-buffered -v "2" | grep -v "1"
3
3
3
3
... ...
// stdbuf도 테스트. 잘나옴.
[root@localhost]# while sleep 0.1; do echo $((1 + RANDOM % 3)); done | stdbuf -oL grep -v "2" | grep -v "1"
3
3
3
3
... ...
.
'Linux' 카테고리의 다른 글
[Linux] lsof - 파일(소켓) 디스크립터 확인 예제 (0) | 2020.05.18 |
---|---|
[Bash] Bash 단축키 (0) | 2020.05.15 |
[Linux] 스왑 메모리 사용하는 프로세스 확인 (looking for process using swap memory) (0) | 2019.03.21 |
[Bash] Bash Script 에러발생해도 다음 스크립트 계속 실행하는 방법 (2) | 2016.11.22 |
[Linux] Bash shell 괄호확장 (brace expansion) (0) | 2016.03.31 |