Linux 표준 스트림과 리다이렉션

리눅스 표준 스트림

리눅스의 리다이렉션에 대해 궁금해져서 이를 정리하기 위해 글을 작성합니다.

표준 스트림

위키피디아에 따르면 표준 스트림을 다음과 같이 정의하고 있습니다.

유닉스 및 유닉스 계열 운영 체제에서 프로그램(프로세스)과 환경(주변기기) 사이에 미리 연결된 입출력 통로

위키백과 - Standard stream

리눅스에서 프로그램(프로세스) 실행시 기본적으로 3개의 스트림이 자동적으로 열립니다.
입력을 위한 스트림 (Standard input, stdin), 출력을 위한 스트림 (Standard output, stdout), 오류 메시지 출력을 위한 스트림 (Standard error, stderr)이며, 이를 표준 스트림이라고 부릅니다.

사용자가 터미널에 접속해 로그인하면 대화형 Shell이 실행되면서 터미널의 표준 스트림을 상속받습니다. 그리고 Shell을 통해 프로세스가 실행되면 프로세스는 Shell의 스트림을 상속받아서 사용합니다. 즉, 모든 프로세스는 실행될 때, 자신을 실행한 부모 프로세스의 스트림을 상속받습니다.

프로세스가 실행되면 기본적으로 표준 입력 스트림은 키보드와 연결됩니다. 그래서 프로세스는 표준 입준 스트림을 통해 사용자의 입력을 받으며, 입력 받은 내용을 처리합니다.
프로세스에서 출력 될 때에는 표준 출력 스트림을 사용하며, 기본적으로 터미널과 연결됩니다.
마지막 표준 오류 스트림은 프로세스에서 출력 스트림 외에도 오류 메시지나 기타 등등의 내용을 출력하기 위해 존재하는 스트림입니다. 표준 오류 스트림도 기본적으로 터미널과 연결됩니다.

파일 디스크립터 (File descriptor)

파일 디스크립터(FD)는 프로세스가 파일에 접근하기 위해 제공되는 고유 식별자입니다. UNIX에서는 모든 객체를 파일로 관리합니다. 프로세스가 특정 파일에 접근하기 위해서 특정 파일의 디스크립터를 이용하면 해당 파일에 접근할 수 있게 됩니다.
예를 들어, 특정 파일에 접근하거나 새로운 파일을 만들면 커널에서 프로세스에게 파일 디스크립터를 반환해줍니다. 그리고 커널은 사용되고 있는 파일의 디스크립터를 테이블의 형태로 관리합니다.

파일 디스크립터는 ‘Non-negative Integer’이며, 0부터 순차적으로 번호가 부여됩니다. 기본적으로 프로세스가 실행될 때, 표준 입력/출력/오류 스트림를 할당합니다. 그리고 순서대로 0/1/2로 파일 디스크립터 정수가 부여됩니다. 그리고 사용자가 추가로 파일 디스크립터를 사용하는 경우에는 3부터 부여됩니다. 사용하는 플랫폼마다 정해진 OPEN_MAX값까지 디스크립터 정수를 부여할 수 있습니다.

스트림명 약어 파일 디스크립터
표준 입력 스트림 stdin 0
표준 출력 스트림 stdout 1
표준 오류 스트림 stderr 2

리다이렉션

각 스트림은 리다이렉션을 통해 다른 스트림으로 방향을 지정할 수 있습니다.

방향 기호 방법 설명
표준 출력 > 명령어 > 파일 명령어의 표준 출력 스트림을 파일로 설정 (파일로 내용을 출력)
표준 출력(추가) » 명령어 » 파일 명령어의 표준 출력 스트림을 파일로 설정하지만,
파일의 내용에 덮어쓰는게 아닌 추가하는 형태
표준 입력 < 명령어 < 파일 명령어의 표준 입력 스트림을 파일로 설정 (파일로 부터 입력 받음)

표준 출력

표준 출력은 기본적으로 터미널로 설정된 출력 스트림의 방향을 파일로 변경해 터미널에 출력되던 내용이 파일에 출력되게 됩니다. 예를 들어 아래의 이미지와 같이 test.txt라는 텍스트 파일이 존재하고 해당 텍스트 파일에는 표준 출력테스트 라는 내용이 저장되어있습니다. cat 명령어는 파일의 내용을 순차적으로 읽어서 표준 출력 스트림을 통해 출력합니다. cat test.txt라는 명령어를 입력하면 텍스트 파일을 읽어서 해당 내용을 출력합니다. 기본적으로 출력 스트림은 터미널이랑 연결되어있기 때문에 터미널에 내용이 표시되지만, > 리다이렉션 기호를 통해 표준 출력 스트림의 방향을 변경해줍니다. cat test.txt > stdout.txt라고 입력하면 cat test.txt의 출력 스트림이 stdout.txt 파일과 연결되어서 출력 되는 내용이 텍스트 파일에 쓰여지게 됩니다.

stdout test

표준 출력 (추가)

표준 출력과 동일한 내용이지만, 출력될 때의 방식이 다릅니다. 표준 출력(>)과 동일하게 출력 스트림의 방향을 변경하는 것은 동일하나, 변경된 출력 스트림으로 출력시 존재하는 파일이 있다면 내용을 덮어씁니다. >>을 사용하게 되면 기존의 내용을 지우지 않고 뒤에 추가하는 방식으로 동작합니다. cat test.txt >> stdout_add.txt를 처음 실행하면 출력 스트림을 통해 텍스트 파일에 내용이 쓰여집니다. 동일한 명령어를 한번 더 실행하게 되면 이전 내용을 지우지 않고 마지막에 추가되어 동일한 내용이 두 번 나오는 것을 확인할 수 있습니다.

stdout add test

리다이렉션과 파일 디스크립터

출력과 오류를 다르게 파일로 출력하고 싶다면 리다이렉션과 파일 디스크립터를 함께 활용할 수 있습니다.
사용 방법은 명령어 [방향을 바꿀 FD]>[방향으로 설정될 파일의 FD] 파일으로 리다이렉션 기호를 중심으로 좌측에는 스트림의 방향을 바꿀 파일 디스크립터를 명시하고 우측에는 방향으로 지정될 파일의 파일 디스크립터를 명시하면 됩니다. 만약 파일 디스크립터를 생략한다면 기본적으로 파일의 출력 스트림으로 지정됩니다. 예를 들어, myscript.sh를 실행시 출력은 stdout.txt라는 파일에 쓰고 오류는 stderr.txt라는 파일에 쓰고 싶다면 아래와 같이 사용하면 됩니다.

$ ./myscript.sh > stdout.txt 2> stderr.txt
(=$ ./myscript.sh 1> stdout.txt 2> stderr.txt)

만약 오류만 출력하고 싶다면 표준 오류 스트림의 방향만 지정해주면 됩니다.

$ ./myscript.sh 2> stderr.txt

하지만, 표준 출력 스트림은 터미널에 출력되기 때문에 출력을 하고 싶지 않다면 /dev/null로 표준 출력 스트림을 보내면 됩니다.

$ ./myscript 2> stderr.txt > /dev/null
  • /dev/null은 아무 것도 존재하지 않는 특별한 파일입니다. 이 파일에 쓰여지는 데이터는 모두 버려지지만, 정상적으로 쓰기 작업이 종료됐다고 인식됩니다. 이러한 빈 파일을 비트 버킷 또는 블랙홀이라고 부릅니다.

스트림의 방향으로 여러 스트림을 설정할 수 있는 기호 &

지금까지 알아본 내용으로는 표준 출력 스트림과 표준 오류 스트림의 방향을 변경하려면 각 스트림의 방향에 대해 명시해야했습니다. 동일한 파일로 두 개의 스트림을 설정하고 싶다면 & 기호를 사용하면 됩니다. 예를 들어, myscript.sh가 실행될 때, 표준 출력/오류 스트림을 result.log라는 파일에 내용을 쓰고 싶다면 기존에는 아래와 같이 두 개의 스트림 모두 result.log에 쓰라고 명시해야했습니다.

$ ./myscript > result.log 2> result.log

& 기호를 사용한다면 조금 더 간편하게 명시할 수 있습니다. 사용 방법은 명령어 [방향을 바꿀 FD]> 파일 [추가로 방향을 바꿀 FD]>&[방향으로 설정될 파일의 FD] 라고 사용하면 됩니다. 아래는 myscript.sh가 실행될 때, 출력 되는 내용을 result.log 파일에 쓰고, 추가로 표준 오류 스트림을 표준 출력 스트림을 통해 출력하겠다라는 의미가 됩니다.

$ ./myscript > result.log 2>&1
  • & 기호를 사용할 경우 띄어쓰기를 사용하면 문법 에러가 발생합니다.
    • 2 > &1 (X)
    • 2 >&1 (X)
    • 2> &1 (X)
    • 2>& 1 (X)
    • 2>&1 (O)

참고