리다이렉션

stdout

프로세스를 실행하면서 로그 경로를 지정할 때 아래와 같은 명령어를 종종 사용한다.

command > out.log 2>&1

이 명령어의 의미는 아래와 같다:

stdout은 out.log로 보내고,
stderr도 stdout이 가리키는 곳, out.log로 보내라

command > out.log 부분을 조금 뜯어서 보면:

command > out.log

stdout을 out.log 파일로 보내라는 의미이고, 이는 보다 명시적으로 아래와 같이 작성할 수 있다.

command 1> out.log

결국 >1> 와 같다고 볼 수 있다.

stderr

위 명령어를 실행하다가 에러가 발생하는 경우, 에러는 out.log로 가지 않고 터미널에 출력된다. stderr에 대한 경로를 별도로 작성하지 않았기 때문이다.

command 1> out.log 2> error.log

위와 같이 작성하면 stdout은 out.log 로, stderr는 error.log 로 보낸다는 뜻이다.

위 명령어를 축약하면 아래와 같이 작성할 수 있다.

command > app.log 2>&1

stdout을 app.log로 보내고, 2(stderr)는 1(stdout)으로 보낸다. 결국 stdout과 stderr을 보두 app.log로 보낸다는 뜻이다. 하나씩 뜯어보면 아래와 같이 정리할 수 있다.

참고로 > 는 기본적으로 “덮어쓰기”로 동작한다. 파일을 대상으로 하는 경우 해당 파일의 모든 내용이 날아가고 새롭게 작성된다. >> 는 기존 내용 뒤에 이어서 작성한다.

프로그램이 실행되면 기본적으로 3개의 fd가 열린다:

fd란 File Descriptor의 줄임말이고 프로세스가 파일, 터미널, 파이프, 소켓 등을 다룰 때 사용하는 번호표이다. 만약 command 2>1 와 같이 사용한다면 stderr는 1 이라는 파일이 생성되면서 그곳에 쓰여질 것이다.

다시 돌아와서, > 은 리다이렉션이라고 했다. 그리고 >1> 은 같은 말이다. 그럼 아래 명령어를 실행하면 어떻게 될까?

command 2>&1 > out.log
  • 2(stderr)를 fd 1이 가리키는 곳(터미널)로 보내고 : 에러가 터미널에 출력된다.

  • 1> 리다이렉션에 의해 stdout을 out.log로 보낸다

리다이렉션은 왼쪽부터 순차적으로 처리되기 때문에 fd 1이 아직 터미널을 가리키고 있는 상황에서 2>&1로 보내는 것은 의도대로 동작하지 않는다.

출력 버리기

/dev/null 은 리눅스의 특수파일이다. 여기에 기록한 내용은 전부 버려진다. 따라서 stdout을 버리고 싶다면 아래와 같이 쓸 수 있다.

command > /dev/null

stderr를 버리고 싶다면? 아래와 같이 쓴다.

command 2> /dev/null

둘 다 버리고 싶다면 위에서 설명한 방식을 응용하여 아래와 같이 쓸 수 있다.

command > /dev/null 2>&1

파이프(|)와 같이 쓰는 경우

파이프를 사용하면 stdout만 다음 명령어로 넘겨지기 때문에 아래와 같은 명령어에서는 에러 출력의 “error” 키워드를 잡을 수 없다.

command | grep error

이 때는 stderr를 fd 1(stdout)으로 보내고 파이프로 넘기면 된다.

command 2>&1 | grep error

대표 패턴

백그라운드에서 서버를 띄울 때 아래 패턴을 많이 쓴다.

nohup ./app >> app.log 2>&1 &

모든 출력을 app.log에 저장하고 백그라운드로 실행하며 기존 app.log에 이어쓰기 함