Linux에서 stdin, stdout 및 stderr이란 무엇입니까?

Linux 명령어를 실행할 때 생성되는 데이터 흐름에는 표준 입력(stdin), 표준 출력(stdout), 표준 오류(stderr) 이렇게 세 가지가 있습니다. 스크립트가 파이프를 통해 연결되거나 리디렉션되는지 여부를 이 스트림들을 통해 파악할 수 있습니다. 그 방법을 상세히 살펴보겠습니다.

데이터 흐름의 두 지점

Linux 및 Unix 계열 운영체제를 배우기 시작하면 stdin, stdout, stderr이라는 용어를 자주 접하게 됩니다. 이 세 가지는 명령어 실행 시 설정되는 표준 데이터 스트림입니다. 컴퓨팅에서 스트림이란 데이터 전송이 가능한 통로를 의미하며, 여기서는 텍스트 데이터를 의미합니다.

마치 물 흐름처럼 데이터 흐름에는 시작점과 끝점이 존재합니다. Linux 명령어를 실행하면 각 스트림은 한쪽 끝을 제공하며, 다른 한쪽 끝은 명령어를 실행한 쉘에 의해 결정됩니다. 명령어가 터미널 창에 직접 연결될 수도 있고, 파이프를 통해 다른 명령어에 연결되거나, 파일 또는 다른 명령어로 리디렉션될 수도 있습니다.

Linux의 표준 스트림

Linux에서 stdin은 표준 입력 스트림으로, 텍스트를 입력으로 받아들입니다. 명령어에서 쉘로 텍스트 출력을 보내는 경로는 stdout(표준 출력) 스트림입니다. 명령어 실행 중 발생한 오류 메시지는 stderr(표준 오류) 스트림을 통해 전송됩니다.

즉, 두 개의 출력 스트림(stdout, stderr)과 하나의 입력 스트림(stdin)이 존재합니다. 오류 메시지와 일반적인 출력은 각각 별도의 통로를 통해 터미널 창으로 전달되기 때문에 서로 독립적으로 처리할 수 있습니다.

파일처럼 처리되는 스트림

Linux에서는 대부분의 요소가 파일처럼 취급됩니다. 스트림 역시 마찬가지입니다. 파일에서 텍스트를 읽고 파일에 텍스트를 쓰는 작업 모두 데이터 스트림을 사용합니다. 따라서 데이터 스트림을 파일처럼 처리하는 개념은 자연스럽게 받아들여집니다.

프로세스에서 사용되는 각 파일에는 고유한 식별 번호가 부여되는데, 이것을 파일 디스크립터라고 합니다. 파일 관련 작업을 수행할 때마다 파일 디스크립터는 해당 파일을 식별하는 데 사용됩니다. 파일 기술자.

다음 값들은 stdin, stdout, stderr에 항상 사용됩니다.

0: 표준 입력
1: 표준 출력
2: 표준 오류

파이프 및 리디렉션에 대한 반응

어떤 주제를 쉽게 소개하기 위해 종종 단순화된 버전을 가르칩니다. 예를 들어, 영어 문법에서 “I는 E 앞에, C 뒤에는 예외”라는 규칙이 있지만, 실제로는 이 규칙에 대한 예외가 더 많습니다.

마찬가지로 stdin, stdout, stderr에 대해 이야기할 때, 프로세스는 세 가지 표준 스트림이 어디로 가는지 알 필요도 없고 관심도 없다는 공리가 흔히 사용됩니다. 프로세스는 출력이 터미널로 가는지, 파일로 리디렉션되는지, 입력이 키보드에서 오는지, 아니면 다른 프로세스의 파이프를 통해 들어오는 것인지 알 필요가 있을까요? 실제로 프로세스는 알 수 있고, 필요하다면 소프트웨어 개발자가 해당 기능을 추가했을 경우, 그에 따라 동작을 변경할 수도 있습니다.

이러한 동작 변화는 쉽게 확인할 수 있습니다. 다음 두 명령어를 실행해 보십시오.

ls

ls | cat

ls 명령어는 출력이 다른 명령어로 파이프될 때 다른 방식으로 동작합니다. 단일 열 출력으로 전환하는 것은 ls 명령어 자체이며 cat 명령어가 변환을 수행하는 것이 아닙니다. 출력이 리디렉션되는 경우에도 ls 명령어는 유사하게 동작합니다.

ls > capture.txt

cat capture.txt

stdout 및 stderr 리디렉션

오류 메시지를 별도의 스트림으로 전달하는 것은 여러 이점을 제공합니다. 명령의 출력(stdout)을 파일로 리디렉션할 때 오류 메시지(stderr)를 터미널 창에서 계속 볼 수 있어 필요한 경우 오류에 대응할 수 있습니다. 또한 오류 메시지가 stdout이 리디렉션된 파일에 섞여 들어가는 것을 방지합니다.

텍스트 편집기에 다음 내용을 입력하고 error.sh라는 이름으로 저장하십시오.

#!/bin/bash

echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

다음 명령어를 사용하여 스크립트를 실행 가능하게 만드십시오.

chmod +x error.sh

스크립트의 첫 번째 줄은 stdout 스트림을 통해 터미널 창에 텍스트를 출력합니다. 두 번째 줄은 존재하지 않는 파일에 접근을 시도하여 stderr을 통해 오류 메시지를 생성합니다.

다음 명령어로 스크립트를 실행해 보십시오.

./error.sh

터미널 창에 stdout과 stderr 두 가지 출력 스트림이 모두 표시됩니다.

이제 출력을 파일로 리디렉션해 보겠습니다.

./error.sh > capture.txt

stderr을 통해 전달되는 오류 메시지는 여전히 터미널 창에 출력됩니다. 파일 내용을 확인하면 stdout 출력이 파일로 이동했음을 알 수 있습니다.

cat capture.txt

stdout 출력이 파일로 리디렉션되었습니다.

> 리디렉션 기호는 기본적으로 stdout에 적용됩니다. 숫자 파일 디스크립터를 사용하여 리디렉션하려는 표준 출력 스트림을 지정할 수 있습니다.

stdout을 명시적으로 리디렉션하려면 다음 리디렉션 명령어를 사용하세요.

1>

stderr을 명시적으로 리디렉션하려면 다음 리디렉션 명령어를 사용하세요.

2>

다시 테스트를 시도하고 이번에는 2>를 사용하겠습니다.

./error.sh 2> capture.txt

오류 메시지가 리디렉션되고, 표준 출력 에코 메시지가 터미널 창에 출력됩니다.

stderr 메시지는 capture.txt 파일에 저장됩니다.

stdout과 stderr을 동시에 리디렉션

만약 stdout과 stderr을 개별적으로 파일로 리디렉션할 수 있다면, 두 스트림을 동시에 서로 다른 파일로 리디렉션하는 것도 가능할까요?

물론 가능합니다. 다음 명령어는 stdout을 capture.txt 파일로 보내고, stderr을 error.txt 파일로 보냅니다.

./error.sh 1> capture.txt 2> error.txt

출력 스트림(stdout과 stderr)이 모두 파일로 리디렉션되었기 때문에 터미널 창에는 아무런 출력도 표시되지 않습니다. 마치 아무 일도 없었던 것처럼 명령 프롬프트로 돌아갑니다.

각 파일의 내용을 확인해 보겠습니다.

cat capture.txt
cat error.txt

stdout과 stderr을 동일한 파일로 리디렉션

각 표준 출력 스트림을 별도의 파일로 이동시킬 수 있다는 것을 확인했습니다. 그렇다면 stdout과 stderr을 같은 파일로 보내는 것도 가능할까요?

다음 명령어로 이를 달성할 수 있습니다.

./error.sh > capture.txt 2>&1

하나씩 살펴봅시다.

./error.sh: error.sh 스크립트 파일을 실행합니다.
> capture.txt: stdout 스트림을 capture.txt 파일로 리디렉션합니다. >는 1>의 단축형입니다.
2>&1: &> 리디렉션 명령어를 사용합니다. 이 명령어를 사용하면 특정 스트림이 다른 스트림과 동일한 대상으로 보내지도록 쉘에 지시할 수 있습니다. 여기서는 “스트림 2(stderr)를 스트림 1(stdout)이 리디렉션되는 동일한 대상으로 리디렉션한다.”고 지정합니다.

터미널에 보이는 출력은 없습니다.

capture.txt 파일을 확인하여 내용물을 살펴봅시다.

cat capture.txt

stdout과 stderr 스트림 모두 동일한 파일로 리디렉션되었습니다.

스트림의 출력을 자동으로 무시하려면 출력을 /dev/null로 보내면 됩니다.

스크립트 내에서 리디렉션 감지

명령어가 스트림이 리디렉션되고 있는지 감지하고 그에 따라 동작을 변경할 수 있다는 것을 이미 알아보았습니다. 그렇다면 스크립트 내에서도 동일한 작업을 수행할 수 있을까요? 물론 가능합니다. 그리고 이는 이해하고 사용하기 매우 쉬운 기술입니다.

텍스트 편집기에 다음 내용을 입력하고 input.sh로 저장하십시오.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

다음 명령어를 사용하여 실행 가능하게 만드십시오.

chmod +x input.sh

핵심 부분은 대괄호 안의 테스트입니다. -t(터미널) 옵션은 파일 디스크립터와 연결된 파일이 터미널 창인 경우 true(0)를 반환합니다. stdin을 나타내는 파일 디스크립터 0을 테스트 인수로 사용했습니다.

stdin이 터미널 창에 연결되어 있으면 테스트가 참으로 판명됩니다. stdin이 파일 또는 파이프에 연결되어 있으면 테스트는 실패합니다.

텍스트 파일을 사용하여 스크립트에 대한 입력을 생성할 수 있습니다. 여기서는 dummy.txt라는 파일을 사용합니다.

./input.sh < dummy.txt

출력 결과에서 스크립트는 입력이 키보드가 아닌 파일에서 온다는 것을 인식합니다. 스크립트의 동작을 이에 맞게 변경할 수 있습니다.

이번에는 파일 리디렉션을 사용하여 시도해 보았으니, 파이프를 사용하여 시도해 보겠습니다.

cat dummy.txt | ./input.sh

스크립트는 입력이 파이프를 통해 전달된다는 것을 인식합니다. 보다 정확히 말하자면, stdin 스트림이 터미널 창에 연결되어 있지 않다는 것을 인식합니다.

이번에는 파이프나 리디렉션 없이 스크립트를 실행해 보겠습니다.

./input.sh

stdin 스트림이 터미널 창에 연결되어 있고, 스크립트가 그 사실을 정상적으로 보고합니다.

출력 스트림으로 동일한 작업을 확인하려면 새로운 스크립트가 필요합니다. 텍스트 편집기에 다음 내용을 입력하고 output.sh로 저장하십시오.

#!/bin/bash

if [ -t 1 ]; then

echo stdout is going to the terminal window
 
else

echo stdout is being redirected or piped
 
fi

다음 명령어를 사용하여 실행 가능하게 만드십시오.

chmod +x input.sh

이 스크립트에서 중요한 변경 사항은 대괄호 안의 테스트입니다. stdout에 대한 파일 디스크립터를 나타내기 위해 숫자 1을 사용하고 있습니다.

이제 시도해 보겠습니다. cat 명령어를 통해 출력을 파이프할 것입니다.

./output.sh | cat

스크립트는 출력이 터미널 창으로 직접 이동하지 않는다는 것을 인식합니다.

파일로 출력을 리디렉션하여 스크립트를 테스트할 수도 있습니다.

./output.sh > capture.txt

터미널 창에는 출력이 없고, 자동으로 명령 프롬프트로 돌아갑니다. 예상대로 작동합니다.

capture.txt 파일 내부를 확인하여 캡처된 내용을 확인할 수 있습니다. 다음 명령어를 사용하십시오.

cat capture.txt

다시 한번, 간단한 테스트를 통해 스크립트는 stdout 스트림이 터미널 창으로 직접 전송되지 않는다는 것을 감지합니다.

파이프나 리디렉션 없이 스크립트를 실행하면 stdout이 터미널 창으로 직접 전달되고 있다는 것을 감지해야 합니다.

./output.sh

그리고 실제로 그렇게 표시됩니다.

의식의 흐름

스크립트가 터미널 창에 연결되어 있는지, 파이프를 통해 연결되어 있는지, 리디렉션되고 있는지 여부를 파악하는 방법을 배웠으므로, 상황에 따라 스크립트의 동작을 조정할 수 있습니다.

로그 및 진단 출력은 화면으로 출력되는지 파일로 출력되는지에 따라 자세한 정도를 조절할 수 있습니다. 오류 메시지는 일반적인 프로그램 출력과 다른 파일에 기록할 수도 있습니다.

일반적으로 더 많은 지식은 더 많은 선택권을 제공합니다.