리눅스 리다이렉션
2>&1
는 표준에러를 표준출력으로 Redirection 하라는 의미이다!
보통 프로그램에서 에러가 발생하면 어떻게 되는가? 화면에 에러 메시지를 출력해 사용자에게 경고를 주게 된다. 이러한 기본적인 에러 동작에 대해 사용자가 임의로 재지정하여, 오류가 발생해도 경고메시지 출력을 내 마음대로 조정할 수 있는데 이것을 표준 에러 리다이렉션이라고 부른다.
그래서 2>&1
이라는 것은 에러가 발생하면 프로그램이 작동을 멈추거나 꺼지지 않게 하고, 대신 에러 내용을 표준 출력 동작으로 행동하게 하여 프로그램에 오류가 있더라도 화면에 오류 메시지를 출력하지 말고 파일이나 다른 작업으로 처리하여 계속 실행하라는 의미로도 해석이 가능하다.
리다이렉션 기호 조합
리다이렉션 (입출력 재지정)
리다이렉션(입출력 재지정)이란 일반적인 키보드 입력, 화면 출력을 사용하지 않고 사용자 마음대로 입력, 출력을 재지정 하겠다는 말이다.
본래 표준입력은 기본적으로 키보드에서 직접 받아오고, 표준 출력은 기본적으로 따로 디스크에 저장하지 않고 화면에 출력된다.
이러한 기본적인 하드웨어적인 입출력 동작을 변경한다는 것이다.
대표적으로 키보드에서 입력을 받지않고 파일내용을 입력으로 받거나, 내용을 화면에 출력하지않고 파일에 저장하는 것이 있다.
리다이렉션 기호 | 방향 | 설명 |
---|---|---|
> | 표준 출력 | 명령 > 파일: 명령의 결과를 파일로 저장 |
>> | 표준 출력(추가) | 명령 >> 파일: 명령의 결과를 기존 파일 데이터에 추가 |
< | 표준 입력 | 명령 < 파일: 파일의 데이터를 명령에 입력 |
파일 디스크립터
표준 입력과 표준 출력 그리고 표준 에러를 쉘이나 시스템 프로그래밍에서는 숫자로도 표현할 수 있는데 이를 파일 디스크립터라고 한다.
유닉스 시스템은 일반적인 파일부터 디렉토리, 소켓, 파이프, 블록/캐릭터 디바이스 등등 모든 객체들을 파일로 관리하는데, 쉘은 작업중 필요한 파일에 일련번호를 붙여서 관리한다.
이 일련번호가 파일 디스크립터며, 표준 입출력 장치도 이 일련번호로 제어된다.
그래서 사용자는 이 일련번호를 이용하여 입출력 장치를 변경(리다이렉션) 할수 있는 것이다.
파일디스크립터 | 이름 | 용도 | 표준장치 |
---|---|---|---|
0 | stdin | 명령어에 입력될 내용을 저장 | 키보드 |
1 | stdout | 명령어에서 출력될 내용을 저장 | 화면 |
2 | stderr | 명령어에서 출력될 에러메시지를 저장 | 화면 |
다양한 리다이렉션 기호 조합
$ 명령 >& 파일명 # : 명령이 실행된 표준 출력의 결과와 에러를 파일로 출력
$ 명령 >>& 파일명 # : 명령이 실행된 표준 출력의 결과와 에러를 파일로 덧붙여 출력
$ 명령 >! 파일명 # : 파일의 존재 유무와 상관없이 생성하고 명령이 실행된 표준 출력의 결과를 파일로 출력
$ 명령 >>! 파일명 # : 파일의 존재 유무와 상관없이 생성하고 파일에 덧붙여 출력
$ 명령 >&! 파일명 # : 파일의 존재 유무와 상관없이 생성하고 명령이 실행된 표준 출력의 결과와 에러를 파일로 출력
$ 명령 >>&! 파일명 # : 파일의 존재 유무와 상관없이 생성하고 명령이 실행된 표준 출력의 결과와 에러를 파일에 덧붙여 출력
$ 명령 1> 파일명 # : 일련번호 1쓰나 안쓰나 어쨋든 출력을 의미하기에 '명령 > 파일명' 와 같다.
$ 명령 2> err.txt # : 만일 명령을 실행하다 에러가 나면, 에러를 화면에 출력하지 말고 파일에 기록하도록 재지정
$ 명령 2>> err.txt # : 오류가 나면 에러내용을 계속 파일에 덧붙여 추가한다.
$ 명령 2>&1 파일명 # : 표준에러를 표준출력 동작으로 재지정 하라는 뜻
>| # : 출력을 리다이렉션 할 때 NOCLOBBER 설정을 무시한다
<> filename # : 장치 파일(/dev)이면, 표준 출력, 표준 입력 등에 모두 사용한다.
2>&1 이해하기
표준에러를 표준출력 동작으로 재지정하는 것에 대해 배워보았다.
하지만 어떤식으로 사용되는지 아직도 감이 잘 안온다.
다음 명령어 예제로 정리해보자
$ ls > log.txt
# ls 명령어 결과를 화면에 출력하는 표준 출력 동작을 재지정해 log.txt에 출력
# 즉, 저장하도록 한다.
$ cat log.txt
log.txt
test
test.sh
공개
다운로드
문서
바탕화면
비디오
사진
서식
음악
정상적으로 디렉토리 리스트 내용이 파일에 잘 들어갔다.
그럼 명령어가 에러나면 어떻게 될까?.
-JJA
라는 ls 명령어에 존재하지 않는 옵션을 줘서 에러를 발생 시켜보도록 하자.
$ ls -JJA > log.txt
ls: 부적절한 옵션 -- 'J'
자세한 정보는 'ls --help'를 입력하십시오.
존재하지 않는 명령어의 옵션을 줬으니 당연하게도 오류메시지가 화면에 출력되며 사용자에게 경고를 주고있다.
log.txt 파일을 조회해보면 빈 파일로 나오게된다
그럼 만약 명령어를 실행 했을 때 에러가 발생하면 오류메시지를 출력하는게 아니라, 에러를 아예 파일에 저장하게 하는 방법은 어떨까?
$ ls -JJA 2> log.txt
$ cat log.txt
ls: 부적절한 옵션 -- 'J'
자세한 정보는 'ls --help'를 입력하십시오.
log.txt 안에 오류 내용이 삽입 된 것을 확인할 수 있다. 화면에 에러가 요란하게 출력이 안되고 조용히 파일에 기록되어 사용성이 좋아졌다.
하지만 위 처럼 리다이렉션을 활용하면 조금 치명적인 문제점이 하나 있다.
표준 에러에 대한 내용 밖에 파일에 기록할 수 없다는 점이다. 아래 예시를 보자
$ ls -lh 2> log.txt
-rw-r--r-- 1 bandit bandit 0 8월 30일 16:01 log.txt
$ cat log.txt
아무 내용도 기록되지 않는다. 예시를 보듯이 출력 내용이 파일안에 안 들어가고 화면에 그대로 나오고 있는 것을 알 수가 있다 ..
- 표준 출력은 정상적인 명령어의 결과 내용의 출력을 화면에서 파일로 바꾼 것이고
- 표준 에러는 오류메시지 출력을 화면에서 파일로 바꾼 것이다.
따라서 2> 표준 에러 재지정은 오류메시지만 취급하기 때문에 정상적인 명령에 대해서 아무런 작업을 하지 않는 것이다.
그렇다면 명령어가 성공하면 success.log 파일에 정상적인 출력 내용을 넣고 반대로 명령어가 실패하면 에러 출력 내용을 fail.log에 기록하고 싶다면 어떻게 하면 좋을까?
$ ls -lh 1>success.log 2>fail.log
# 만일 명령어를 실행해서 성공하면 표준 출력 재지정을 통해 파일에 기록하고
# 명령어가 실패하면 표준 에러 재지정을 통해 파일에 기록하도록 조정
$ cat success.log
합계 36K
-rw-r--r-- 1 bandit bandit 0 8월 30일 16:04 fail.log
-rw-r--r-- 1 bandit bandit 0 8월 30일 16:01 log.txt
사진에서 보듯이, 명령어가 성공하면 결과 내용이 파일에 기록되었고,
명령어가 실패하면 오류메세지가 파일에 기록됨을 확인 할 수 있다.
명령어가 성공하든 실패하든 둘다 파일에 기록되도록 설정에 성공한 것이다.
그리고 2>&1
는 위의 명령어(1> success.log 2> fail.log
)를 함축한 버전이라고 보면 될 것 같다.
$ ls -lh > all_log.txt 2>&1
참고로 2>&1
는 crontab에서 요긴하게 쓰인다.
만일 표준 에러를 재지정 하지 않으면, 어떤 다른 계정의 사용자가 리눅스를 이용하다 cron에 의해 에러가 나는 스크립트가 갑자기 실행되서 터미널에 엉뚱한 에러 메시지가 출력될 수도 있는 상황이 발생할 수 있다.
$ crontab -l
* * * * * /home/user/script.sh >> /tmp/desc/happy.txt 2>&1
# script.sh 스크립트 실행결과를 표준 출력 재지정을 통해 happy.txt 파일에 결과 값을 append 하고,
# 만일 스크립트가 에러가 나면 2>&1를 통해 그대로 표준 출력 동작(파일 기록)을 행하게 한다
반대로 표준 에러가 파일에 기록되는 것이 싫다면 아래처럼 하면 된다
* * * * * /home/user/script.sh >> /tmp/desc/happy.txt 2>/dev/null
/dev/null은 /dev/null 이해하기에서 배워보도록 하자
/dev/null 이해하기
$ ./script.sh 1> success.log 2> /dev/null
/dev/null은 쓰레기통 또는 블랙홀 이라고 이해하면 된다.
해당 경로에 보내지는 모든 데이터 및 파일들은 블랙홀에 빨려가듯 흔적도 없이 사라지게 된다.
즉, /dev/null로 결과를 보낸다는 것은 데이터를 죄다 없애 깔끔하게 화면에 표시하지 않겠다는 것을 의미한다.
/dev/null이 언제 쓰이는가?
특정 명령어를 실행한 후 출력이 필요 없는 경우에는 과감히 결과값을 /dev/null로 보내버리면 된다.
자 그럼 이제 아래 명령어또한 해석이 바로 될 것이다.
$ bash ./script.sh 1>/dev/null 2>&1
script.sh 을 실행하여 나온 표준 출력을 /dev/null로 보내고 표준 에러 또한 표준 출력으로 전환해 같이 /dev/null로 버려지게 되는 것이다.