UNIX/유닉스 공통

[스크랩] 리눅스 표준 출력 및 에러 재지향 (출력과 저장을 동시에, tee)

99iberty 2014. 1. 29. 13:38

 

http://linuxism.tistory.com/777

 

 

리눅스/유닉스의 콘솔/터미널의 bash 쉘 등에서는 재지향(Redirection)이라는 방법으로, 각종 명령어 출력 결과를 파일로 저장할 수 있습니다.

예를 들어 ls 명령의 출력 결과를, 연필로 옮겨쓰는 대신에

ls > out.txt


라고 하면 out.txt 라는 파일로 ls 명령의 결과가 간단히 저장됩니다. ls뿐 아니라 다른 모든 명령들도 마찬가지입니다.

> 이런 기호는, 글자들의 출력 방향을 화면이 아닌, 파일 같은 다른 데로 전환시키는 것입니다.


에러 메시지까지 파일로 저장하는 방법


그런데 위의 재지향 방법으로는 에러 메시지 출력은 저장되지 않습니다. 에러 메세지까지 파일로 저장되어 버리면, 에러가 났는지 알 수 없기 때문입니다. 기술적으로 말하자면, 일반 문자열은 "표준 출력(Standard Output)"으로 출력되고, 에러는 "표준 에러 출력(Standard Error)"으로 출력되기에 재지향이 안되는 것입니다.

에러까지 재지향하여 파일로 저장하는 방법이 있습니다.

ls ewyrsyrwyy &> out.txt


이렇게 &> 기호로 재지향하면 됩니다. ewyrsyrwyy 라는 이름의 파일이 없을 것이기에, ls 에서 그런 파일을 찾을 수 없다(No such file or directory)고 에러가 납니다. 그 에러 메시지가 out.txt 라는 파일로 저장됩니다.

 


출처 - http://mwultong.blogspot.com/2006/10/linux-unix-output-to-file.html


===================================================================================


 

1. 표준 입출력
linux에서는 프로그램 실행시 표준 입출력에 관한 파일디스크립터(fd)가 생성되게 된다.

기본적으로 표준 입력(0), 표준출력(1), 표준에러(2)로 설정된다.
그렇기 때문에 실제 코드상에서 fopen등으로 하나의 파일디스크립터를 얻게되면 대부분 3번부터 return한다.

표준입력 (stdin) 0 키보드
표준출력 (stdout) 1 모니터(터미널)
표준에러 (stderr) 2 모니터(터미널)

이와 같은 구조에서 입출력 재지정이란, in/out을 다른 곳으로 변경함을 의미하며. 일반적으로는 파일로 재지정이 가능하다.

2. redirection
linux에서 make > log 와 같은 구문을 본적이 있을텐데 이것이 바로 입출력 재지정을 의미한다.

재지정을 위한 연산자는 아래 표와 같다.

재지정구분 연산자 의미 사용예의 의미
출력 > write % 명령수행 > 기록할 파일이름
>> append % 명령수행 >> 추가할 파일이름
입력 < read % 명령수행 < 읽어올 파일이름
에러출력 2> sh,ksh,bash $ 명령수행 2> 기록할 오류파일이름
>& csh % 명령수행 >& 기록할 오류파일이름

예를 들기 위하여 간단한 shell script 하나를 작성해 보자.

  1. #!/bin/sh
  2. echo "STDIN START"
  3. ls -al
  4. echo "STDIN END"
  5. echo "STDERR START">&2
  6. ls e
  7. echo "STDERR END">&2
내용을 살펴보면 위의 코드는 ls -al와 ls k라는 명령을 수행한다.
ls -al의 경우 정상적으로 수행되지만, ls e라는 것은 정상적으로 수행되지 않고 오류를 출력할 것이다.

위 script를 일반적으로 수행해 보면 다음과 같이 나온다.

 

# ./a.sh
STDIN START
합계 12
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 12:53 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 12:52 ..
-rwxrwxrwx 1 jelee9 jelee9 104 2010-05-31 12:52 a.sh
STDIN END
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
#

stdin과 stdout이 둘다 모니터로 출력되도록 되어 있으므로 두가지 모두 정상적으로 출력된다.

이제 redirection을 통하여 표준출력을 변경해 보도록 하자.

#./a.sh > stdin_log
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
# cat stdin_log
STDIN START
합계 16
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 12:54 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 12:52 ..
-rwxrwxrwx 1 jelee9 jelee9 104 2010-05-31 12:52 a.sh
-rw-r--r-- 1 jelee9 jelee9 12 2010-05-31 12:54 stdin
STDIN END
#

STDIN의 경우에는 stdin_log 파일에만 출력되며 stderr만 모니터로 출력되는 것을 알 수 있다.
(./a.sh > stdin_log대신에 ./a.sh 1>stdin_log 모두 동일한 결과를 갖는다.)

이번엔 반대로 표준에러를 변경해 보도록 하자.

# ./a.sh 2>stderr_log
STDIN START
합계 16
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 12:54 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 12:52 ..
-rwxrwxrwx 1 jelee9 jelee9 104 2010-05-31 12:52 a.sh
-rw-r--r-- 1 jelee9 jelee9 12 2010-05-31 12:54 stdin
STDIN END
#cat stderr_log
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
#

예상했던 대로 STDIN만 모니터로 출력되며 STDERR는 stderr_log 파일에 저장됨을 알 수 있다.

마지막으로 표준에러를 표준출력으로 변경해보자.

# (./a.sh 2>&1) > log
# cat log
STDIN START
합계 16
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 13:30 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 13:20 ..
-rwxrwxrwx 1 jelee9 jelee9 102 2010-05-31 13:26 a.sh
-rw-r--r-- 1 root root 12 2010-05-31 13:30 log
STDIN END
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
#

표준에러(2)를 표준출력(1)으로 redirection한 후에 표준 출력을 log라는 파일에 저장하는 것이다.

이와같이 표준입출력을 재지정하면 원하는 출력을 파일로 저장하도록 할 수 있다.

대부분의 경우에는 build 시에 불필요한 log를 skip 하기 위하여 사용하는 경우가 많다.
(make > /dev/null과 같이 정상적인 build의 경우에는 화면 출력을 하지 않도록 설정)

3. tee 명령어
redirection의 경우에는 입출력의 방향을 변경할 수 있습니다. 그러나 불행하게도 redirection된 내용은 화면상에서 보이지 않게되는 문제점이 있다. 그래서 등장한 것이 tee 명령어 이다.

man page를 살펴보면, read from standard input and write to standard output and files 라고 적혀있다.
말 그대로 stdin을 stdin으로 출력과 동시에 파일에도 저장해주는 기능을 가지고 있다.

일단 한번 사용해 보도록 하자.

# ./a.sh | tee tee_log
STDIN START
합계 20
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 13:35 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 13:20 ..
-rwxrwxrwx 1 jelee9 jelee9 102 2010-05-31 13:26 a.sh
-rw-r--r-- 1 root root 334 2010-05-31 13:30 log
-rw-r--r-- 1 root root 12 2010-05-31 13:35 tee_log
STDIN END
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
# cat tee_log
STDIN START
합계 20
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 13:35 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 13:20 ..
-rwxrwxrwx 1 jelee9 jelee9 102 2010-05-31 13:26 a.sh
-rw-r--r-- 1 root root 334 2010-05-31 13:30 log
-rw-r--r-- 1 root root 12 2010-05-31 13:35 tee_log
STDIN END
#

위와 같이 tee 명령을 사용하면 표준 출력의 경우 화면으로도 줄력되면서 동시에 tee_log 파일에도 기록되는 것을 알 수 있다.

실제로 제 경우에는 build 오류 메세지만 출력하기 위하여 stdin을 /dev/null로 보내도록 하였더니, 정작 error가 나왔을 때 어느 위치를 build 도중에 나온 것인지 모르는 경우가 발생하였다.

그래서 redirection과 tee 조합을 통하여 stdin은 파일에만 출력하도록, stderr의 경우에는 파일과 화면 출력을 동시에 하도록 사용하고 있다.

# (./a.sh >> log ) 2>&1 | tee -a log
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
# cat log
STDIN START
합계 16
drwxr-xr-x 2 jelee9 jelee9 4096 2010-05-31 13:41 .
drwxr-xr-x 31 jelee9 jelee9 4096 2010-05-31 13:20 ..
-rwxrwxrwx 1 jelee9 jelee9 102 2010-05-31 13:26 a.sh
-rw-r--r-- 1 root root 12 2010-05-31 13:41 log
STDIN END
STDERR START
ls: e에 접근할 수 없습니다.: No such file or directory
STDERR END
#

이처럼 redirection과 tee의 조합을 사용하여 약간의 개발의 편리를 추구할 수 있지 않을 까?



ps. 회사에서 일반 log를 모조리 출력하지 않으면,
오래 걸리는 build를 걸어 놨을 경우에...
일 안하고 멍때리는 사람으로 오해받을 소지 많으니 주의할것. ㅡ,,ㅡ;;


출처 - http://www.leejongeun.com/tc/entry/Linux-%ED%91%9C%EC%A4%80%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%9E%AC%EC%A7%80%EC%A0%95

 

 

----------------------------------------------------------------------------------------------------

 

16장 I/O 재지향

 

http://wiki.kldp.org/HOWTO/html/Adv-Bash-Scr-HOWTO/io-redirection.html

 

 

차례
16.1. exec 쓰기
16.2. 코드 블럭 재지향
16.3. 응용

쉘은 항상 기본적으로 표준입력(stdin, 키보드), 표준출력(stdout, 스크린), 표준에러(stderr, 스크린에 뿌려질 에러 메세지) "파일들"을 열어 놓습니다. 이 파일들을 포함해서 열려 있는 어떤 파일이라도 재지향 될 수 있습니다. 재지향이란 간단히 말해서 파일, 명령어, 프로그램, 스크립트, 심지어는 스크립트 속의 코드 블럭(예 4-1, 예 4-2 참고)의 출력을 낚아 채서 다른 파일, 명령어, 프로그램, 스크립트의 입력으로 보내는 것입니다.

열려 있는 파일 각각은 파일 디스크립터(file descriptor)를 할당 받습니다. [1] 표준입력, 표준출력, 표준에러에 해당하는 파일 디스크립터는 각각 0, 1, 2 입니다. 추가적으로 열리는 파일을 위해서 3부터 9까지의 파일 디스크립터가 남겨져 있습니다. 종종, 이 추가적인 파일 디스크립터들중의 하나를 표준입력, 표준출력, 표준에러로 할당해서 임시적인 중복된 링크로 쓰는 것이 유용할 때가 있습니다. [2] 이런 방법을 쓰면 아주 복잡한 재지향이나 파일 디스크립터를 뒤죽 박죽 사용했을 때, 아주 간단하게 원래대로 복구시켜 줍니다(예 16-1 참고).

   >
      # 표준출력을 파일로 재지향.
      # 파일이 없으면 새로 만들고, 있다면 덮어 씁니다.
      ls -lR > dir-tree.list
      # 디렉토리 트리 목록을 파일로 저장해 줍니다.
   : > filename
      # > 는 "filename"의 길이가 0 이 되도록 잘라줍니다.
      # : 는 아무 출력도 안 하는 더미 플레이스홀더(placeholder)로 동작합니다.
   >>
      # 표준출력을 파일로 재지향.
      # 파일이 없으면 새로 만들고, 있으면 파일 끝에 덧붙입니다.
   2>&1
      # 표준에러를 표준출력으로 재지향.
      # 에러 메세지는 표준 출력의 자격으로 스크린에 보내집니다.
   i>&j
      # i번 파일 디스크립터를 j번 파일디스크립터로 재지향.
      # i가 가르키는 파일의 모든 출력은 j가 가르키는 파일로 보내집니다.
   >&j
      # 기본적으로 1번 파일 디스크립터(표준출력)를 j번 파일 디스크립터로 재지향.
      # 모든 표준출력은 j가 가르키는 파일로 보내집니다.
   0<
    <
      # 파일에서 입력을 받도록 해줍니다.
      # ">"와 짝을 이루는 명령어로, 종종 같이 쓰입니다.
      #
      # grep search-word <filename
   [j]<>filename
      # "filename"을 읽고 쓰기용으로 열고 "j"번 파일 디스크립터를 할당합니다.
      # "filename"이 없다면 새로 만듭니다.
      # "filename"이 주어지지 않으면 기본적으로 표준입력인 0번이 할당됩니다.
      #
      # 이를 응용하면 파일의 특정한 위치에 쓰기를 할 수 있습니다.
      echo 1234567890 > File    # "File"에 문자열을 씁니다.
      exec 3<> File       # "File"을 열고 3번 파일 디스크립터를 할당합니다.
      read -n 4 <&3             # 문자 4개만 읽은 다음,
      echo -n . >&3             # 소수점을 쓰고,
      exec 3>&-                 # 3번 파일 디스크립터를 닫습니다.
      cat File                  # ==> 1234.67890
      # 어라, 랜덤 억세스네.
   |
      # 파이프.
      # 프로세스와 명령어를 엮어 주는 일반적인 목적의 툴.
      # ">"와 비슷하지만, 실제로는 좀 더 일반적으로 쓰입니다.
      # 명령어, 스크립트, 파일, 프로그램들을 함께 묶는데 유용하게 쓰입니다.
      cat *.txt | sort | uniq > result-file
      # 모든 *.txt 파일의 출력을 정렬한 다음, 중복되는 줄을 제거하고
      # 마지막으로 그 결과를 "result-file"에 저장.

여러개의 입출력 재지향과 파이프를 하나의 명령어 줄에서 같이 쓸 수 있습니다.

command < input-file > output-file
command1 | command2 | command3 > output-file
예 12-23예 A-10 를 참고.

여러개의 출력 스트림이 한 파일로 재지향 될 수도 있습니다.

ls -yz >> command.log 2>&1
# "ls"의 잘못된 옵션인 "yz"의 결과를 "command.log"로 저장합니다.
# 표준에러가 파일로 재지향 됐기 때문에 어떤 에러 메세지라도 그 파일에 저장됩니다.

파일 디스크립터 닫기

n<&-

n번 입력 파일 디스크립터를 닫아 줍니다.

0<&-, <&-

표준입력을 닫아 줍니다.

n>&-

n번 출력 파일 디스크립터를 닫아 줍니다.

1>&-, >&-

표준출력을 닫아 줍니다.

자식 프로세스는 열려 있는 파일 디스크립터를 상속 받는데 이것 때문에 파이프가 동작합니다. 파일 디스크립터가 상속되길 바라지 않는다면 그 파일 디스크립터를 닫으면 됩니다.

# 파이프로 표준에러만 재지향 하기.
exec 3>&1                              # 표준출력의 현재 "값"을 저장.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # 'ls'와 'grep'을 위해 3번 파일 디스크립터를 닫고,
exec 3>&-                              # 이제, 스크립트 나머지 부분을 위해 닫습니다.
# Thanks, S.C.

I/O 재지향에 대한 더 자세한 소개는 부록 D를 참고하세요.

주석

[1]

파일 디스크립터란 운영체제가 계속 추적할 수 있도록 열려 있는 파일에 할당해 주는 간단한 숫자입니다. 파일 포인터의 간단한 버전이라고 생각하면 됩니다. C 의 파일 핸들(file handle)과 유사한 개념입니다.

[2]

5번 파일 디스크립터를 쓰면 문제가 생길 수 있습니다. Bash가 exec으로 자식 프로세스를 만들 때, 그 자식은 5번 파일 디스크립터를 상속받습니다(Chet Ramey의 이메일 아카이브, SUBJECT: RE: File descriptor 5 is held open을 참고하세요). 이 특별한 파일 디스크립터는 건드리지 않는게 좋습니다.

 

 

 

 

 

 

 

 

 

 

'UNIX > 유닉스 공통' 카테고리의 다른 글

[스크랩] SWAP 공간 늘려주기  (0) 2014.02.12
리눅스 iso 파일 마운트 하는 방법  (0) 2014.02.09
top에서 VIRT와 RES  (0) 2014.01.15
[스크랩] 솔라리스 마운트  (0) 2013.12.10
소스코드 컴파일  (0) 2013.11.29