💻 리눅스 리다이렉션 완벽 정복: 기본부터 고급까지 (실습 예제 포함)
안녕하세요! 리눅스 환경에서 터미널 작업을 하다 보면 명령어의 결과를 파일로 저장하거나, 파일의 내용을 명령어의 입력으로 사용해야 하는 경우가 정말 많습니다. 이때 마법처럼 등장하는 것이 바로 **리다이렉션(Redirection)**입니다! 🧙♂️
오늘은 리눅스 리다이렉션의 다양한 연산자들과 그 의미, 그리고 cat << EOF
와 같은 특별한 용법까지 자세히 알아보고,
각 기능들을 직접 실습해보는 시간을 갖도록 하겠습니다.
셸의 기본 통로: 표준 스트림 (Standard Streams)
리눅스(및 유닉스 계열) 프로그램은 기본적으로 세 가지 표준 데이터 스트림을 사용합니다:
- 표준 입력 (stdin, Standard Input): 프로그램이 데이터를 입력받는 통로입니다. 기본적으로 키보드에 연결됩니다. 파일 디스크립터 번호는
0
입니다. - 표준 출력 (stdout, Standard Output): 프로그램이 일반적인 결과를 출력하는 통로입니다. 기본적으로 화면(터미널)에 연결됩니다. 파일 디스크립터 번호는
1
입니다. - 표준 에러 (stderr, Standard Error): 프로그램이 오류 메시지를 출력하는 통로입니다. 기본적으로 화면(터미널)에 연결됩니다. 파일 디스크립터 번호는
2
입니다.
리다이렉션은 이러한 표준 스트림의 방향을 바꾸는 기술입니다.
🚀 주요 리다이렉션 연산자 파헤치기
이제 본격적으로 다양한 리다이렉션 연산자들을 살펴보고 실습을 통해 익혀보겠습니다.
1. 표준 출력 (stdout) 리다이렉션: 화면 대신 파일로!
명령어의 일반적인 실행 결과를 화면이 아닌 파일로 저장하고 싶을 때 사용합니다.
가. >
(덮어쓰기)
명령어의 표준 출력을 지정된 파일로 보냅니다. 파일이 이미 존재한다면 그 내용을 완전히 덮어씁니다. 파일이 없다면 새로 생성합니다.
- 사용법:
명령어 > 파일명
- 실습:
# 현재 디렉토리 목록을 list.txt 파일에 저장 (덮어쓰기) ls -l > list.txt cat list.txt # 저장된 내용 확인 # 새로운 내용을 list.txt에 저장 (기존 내용 삭제 후 _덮어쓰기_) echo "Hello, Redirection!" > list.txt cat list.txt
나. >>
(이어쓰기)
명령어의 표준 출력을 지정된 파일의 끝에 추가합니다. 파일이 없다면 새로 생성합니다.
- 사용법:
명령어 >> 파일명
- 실습:
# list.txt 파일이 이미 있다고 가정 (없으면 새로 생성) echo "첫 번째 줄입니다." > list.txt cat list.txt # list.txt 파일 끝에 새로운 내용 _추가_ echo "두 번째 줄을 추가합니다." >> list.txt cat list.txt # 현재 날짜와 시간도 추가해볼까요? date >> list.txt cat list.txt
다. >|
(강제 덮어쓰기)
가끔 set -o noclobber
라는 셸 옵션을 설정하여 >
연산자로 기존 파일을 실수로 덮어쓰는 것을 방지할 수 있습니다. 이때, >|
를 사용하면 noclobber
옵션이 설정되어 있어도 강제로 파일을 덮어쓸 수 있습니다.
- 사용법:
명령어 >| 파일명
- 실습:
# noclobber 옵션 설정 (덮어쓰기 방지) set -o noclobber echo "원본 내용" > clobber_test.txt cat clobber_test.txt # '>' 로 덮어쓰기 시도 (에러 발생!) echo "덮어쓰기 시도" > clobber_test.txt # 터미널 출력: bash: clobber_test.txt: cannot overwrite existing file # '>|' 로 강제 덮어쓰기 성공! echo "강제로 덮어쓴 내용입니다." >| clobber_test.txt cat clobber_test.txt # noclobber 옵션 해제 (원상복구) set +o noclobber
2. 표준 입력 (stdin) 리다이렉션: 파일 내용을 명령어로!
파일에 저장된 내용을 명령어의 입력으로 사용하고 싶을 때 활용합니다.
가. <
(입력)
지정된 파일의 내용을 명령어의 표준 입력으로 전달합니다.
- 사용법:
명령어 < 파일명
- 실습:
# 입력으로 사용할 input.txt 파일 생성 echo "첫 번째 데이터 라인" > input.txt echo "두 번째 데이터 라인" >> input.txt echo "마지막 데이터 라인" >> input.txt cat input.txt # input.txt 파일의 내용을 cat 명령어의 입력으로 사용 (cat은 입력을 그대로 출력) cat < input.txt # input.txt 파일의 내용을 wc (word count) 명령어의 입력으로 사용해 라인 수 세기 wc -l < input.txt # 출력: 3
3. Here Document (<< 구분자
): 스크립트 안의 여러 줄 입력
스크립트 내에서 여러 줄의 문자열을 직접 명령어의 표준 입력으로 전달할 때 아주 유용합니다. 마치 문서(Document)가 여기(Here)에 바로 있는 것과 같다고 해서 붙여진 이름입니다.
-
사용법:
명령어 << 사용자지정_구분자 여기에 여러 줄의 내용을 자유롭게 입력할 수 있습니다. 사용자지정_구분자
사용자지정_구분자
는 여러분이 원하는 어떤 문자열이든 될 수 있습니다 (보통EOF
,DELIMITER
,END
등을 많이 사용).- 시작 구분자와 끝 구분자는 정확히 일치해야 합니다.
- 끝 구분자는 해당 줄의 맨 처음에 와야 하며, 앞뒤로 다른 문자나 공백이 없어야 합니다. (주석은 가능:
EOF # 이것은 주석
) - 기본적으로 Here Document 내의 변수(
$VAR
)나 명령어 치환($(command)
)이 일어납니다. 이를 방지하려면 시작 구분자를 작은따옴표나 큰따옴표로 감싸면 됩니다 (예:cat << 'EOF'
또는cat << "EOF"
).
-
실습:
-
기본 사용 (변수 및 명령어 치환):
MY_EDITOR="VSCode" cat << EOF 안녕하세요! 제가 주로 사용하는 편집기는 $MY_EDITOR 입니다. 오늘은 $(date '+%Y년 %m월 %d일 %A') 입니다. 이 내용은 Here Document를 통해 전달되었습니다. EOF # 예상 출력: # 안녕하세요! 제가 주로 사용하는 편집기는 VSCode 입니다. # 오늘은 2025년 05월 09일 금요일 입니다. # 이 내용은 Here Document를 통해 전달되었습니다.
-
파일에 저장하기:
cat << EOF > my_config.txt # 이것은 설정 파일 예시입니다. USER_NAME="guest" TIMEOUT=30 ENABLE_FEATURE_X=true EOF echo "--- my_config.txt 파일 내용 ---" cat my_config.txt
-
변수 치환 방지:
cat << 'EndOfText' # 또는 "EndOfText" 이 안에서는 $PATH 변수나 $(pwd) 명령어가 해석되지 않고 그대로 문자열로 취급됩니다. EndOfText # 예상 출력: # 이 안에서는 $PATH 변수나 $(pwd) 명령어가 # 해석되지 않고 그대로 문자열로 취급됩니다.
🤔
EOF
는 특별한 키워드인가요? 아니요!EOF
는 "End Of File"의 약자로, 파일의 끝을 의미하는 관례적인 표현일 뿐입니다. Here Document에서EOF
는 단순히 입력의 끝을 알리는 구분자(Delimiter) 역할을 합니다.MY_DELIMITER
,STOP_HERE
등 어떤 문자열을 사용해도 동일하게 작동합니다.EOF
가 널리 쓰이는 이유는 그 의미가 직관적이고 전통적으로 많이 사용되었기 때문입니다.💡 Here Document는 언제 사용할까요?
- 스크립트 내에서 여러 줄로 구성된 설정 파일 내용을 동적으로 생성할 때
- 긴 SQL 쿼리를 셸 스크립트에서 실행할 때
- 사용자에게 보여줄 안내 메시지 등을 여러 줄로 깔끔하게 작성할 때
-
4. Here String (<<< 문자열
): 한 줄짜리 즉석 입력
단일 줄의 문자열을 명령어의 표준 입력으로 빠르게 전달하고 싶을 때 사용합니다. echo "문자열" | 명령어
와 유사하지만, 파이프를 위한 별도의 서브 프로세스를 생성하지 않아 약간 더 효율적일 수 있습니다.
- 사용법:
명령어 <<< "여기에 한 줄 문자열 입력"
- 실습:
# "Hello Here String" 문자열을 wc 명령어의 입력으로 전달하여 단어 수 세기 wc -w <<< "Hello Here String example" # 출력: 4 (단어 수) # 간단한 계산을 bc 계산기에 전달 bc <<< "100 / 5 + (3 * 2)" # 출력: 26 # grep으로 특정 패턴 찾기 grep "Linux" <<< "I love Linux and Shell Scripting!" # 출력: I love Linux and Shell Scripting!
5. 표준 에러 (stderr) 리다이렉션: 오류 메시지도 내 마음대로!
명령어 실행 중 발생하는 오류 메시지(표준 에러, 파일 디스크립터 2
)를 원하는 곳으로 보낼 때 사용합니다. 로그 파일 관리에 매우 유용합니다.
가. 2>
(stderr 덮어쓰기)
명령어의 표준 에러를 지정된 파일로 보냅니다. 파일이 존재하면 내용을 덮어씁니다.
- 사용법:
명령어 2> 에러로그_파일명
- 실습:
# 존재하지 않는 파일을 찾으려고 시도 (에러 발생) ls non_existent_file.txt 2> error.log echo "--- error.log 파일 내용 ---" cat error.log # 에러 메시지 확인 # 정상 명령은 화면에 출력되고, 에러만 파일에 저장 ls /etc/passwd non_existent_file.txt > output.txt 2> error_only.log echo "--- output.txt 파일 내용 ---" cat output.txt echo "--- error_only.log 파일 내용 ---" cat error_only.log
나. 2>>
(stderr 이어쓰기)
명령어의 표준 에러를 지정된 파일의 끝에 추가합니다.
- 사용법:
명령어 2>> 에러로그_파일명
- 실습:
# error.log 파일에 에러 메시지 추가 (기존 error.log는 위에서 만들었다고 가정) find /root -name "*.conf" 2>> error.log # 일반 사용자는 /root 접근 권한이 없어 에러 발생 echo "--- error.log 파일 내용 (추가 후) ---" cat error.log
6. 표준 출력과 표준 에러를 한 곳으로!
때로는 일반 출력과 오류 메시지를 모두 같은 파일에 기록하고 싶을 때가 있습니다.
가. &>
또는 >&
(stdout과 stderr을 같은 파일로 덮어쓰기)
명령어의 표준 출력(1)과 표준 에러(2)를 모두 지정된 파일로 보냅니다 (덮어쓰기). Bash 4 버전 이상에서는 &>
가 권장됩니다.
- 사용법:
명령어 &> 파일명
또는명령어 >& 파일명
- 실습:
# 표준 출력과 표준 에러를 모두 all_output.log 에 저장 ls /etc/fstab non_existent_file.txt &> all_output.log echo "--- all_output.log 파일 내용 ---" cat all_output.log
나. &>>
(stdout과 stderr을 같은 파일로 이어쓰기)
명령어의 표준 출력과 표준 에러를 모두 지정된 파일의 끝에 추가합니다. (Bash 4 버전 이상)
- 사용법:
명령어 &>> 파일명
- 실습:
# all_output.log 에 표준 출력과 표준 에러를 모두 추가 echo "--- 다음 로그 섹션 ---" &>> all_output.log date &>> all_output.log ls /another_non_existent_dir &>> all_output.log echo "--- all_output.log 파일 내용 (추가 후) ---" cat all_output.log
다. 전통적인 방식: > 파일명 2>&1
(덮어쓰기) / >> 파일명 2>&1
(이어쓰기)
오래된 셸이나 스크립트 호환성을 위해 알아두면 좋은 방법입니다.
2>&1
의 의미는 "파일 디스크립터 2번(stderr)을 파일 디스크립터 1번(stdout)이 현재 가리키고 있는 곳으로 보내라"는 뜻입니다.
명령어 > 파일명 2>&1
(덮어쓰기)# 표준 출력과 표준 에러를 모두 combined_output.log 에 저장 (덮어쓰기) ls /etc/hosts no_such_file.tmp > combined_output.log 2>&1 echo "--- combined_output.log 파일 내용 ---" cat combined_output.log
명령어 >> 파일명 2>&1
(이어쓰기)# combined_output.log 에 표준 출력과 표준 에러를 모두 추가 echo "--- 추가 로그 ---" >> combined_output.log 2>&1 find /non_existent_dir -type f >> combined_output.log 2>&1 echo "--- combined_output.log 파일 내용 (추가 후) ---" cat combined_output.log
7. 파이프 (|
): 명령어 체인 만들기
파이프는 한 명령어의 표준 출력을 다른 명령어의 표준 입력으로 직접 연결합니다. 리다이렉션과는 조금 다르지만 데이터의 흐름을 제어한다는 점에서 매우 중요합니다. 여러 명령어를 조합하여 복잡한 작업을 간단하게 처리할 수 있게 해줍니다.
- 사용법:
명령어1 | 명령어2 | 명령어3 ...
- 실습:
# 현재 디렉토리 목록 중 'txt'로 끝나는 파일/디렉토리만 필터링 ls -l | grep 'txt$' # 실행 중인 모든 프로세스 정보에서 'bash'라는 단어가 포함된 라인만 찾기 ps aux | grep 'bash' # /etc 디렉토리에 있는 파일 및 디렉토리의 총 개수 세기 ls /etc | wc -l
8. 고급 기술: 파일 디스크립터 직접 다루기
좀 더 세밀하게 스트림을 제어하고 싶을 때 파일 디스크립터를 직접 조작할 수 있습니다.
가. [n]>&[m]
(디스크립터 복제/이동)
파일 디스크립터 n
을 파일 디스크립터 m
의 복사본으로 만듭니다. 즉, n
이 m
과 같은 곳을 가리키도록 합니다.
예를 들어, 2>&1
은 "stderr(2)을 stdout(1)이 가리키는 곳으로 보내라"는 의미입니다.
- 실습:
# 일반 메시지는 output.log, 에러 메시지는 error.log로 분리 저장 # 중괄호 { } 는 명령어 그룹화 { echo "이것은 일반 메시지 (stdout)"; echo "이것은 에러 메시지 (stderr)" >&2; } > output.log 2> error.log cat output.log cat error.log
나. [n]>&-
또는 [n]<&-
(디스크립터 닫기)
파일 디스크립터 n
을 닫습니다. 해당 스트림으로의 출력을 막거나 입력을 받지 않도록 합니다.
- 실습:
# 에러 메시지를 완전히 무시 (어디에도 보내지 않고 그냥 버림) ls non_existent_file.txt 2>&- echo "위 명령어에서 에러가 발생했지만, 화면이나 파일 어디에도 출력되지 않았습니다." # 표준 출력을 닫음 (일반 출력 안 보임) echo "이 메시지는 보이지 않아야 합니다." 1>&- echo "이 메시지는 표준 에러로 출력되므로 보입니다." >&2
9. 프로세스 치환 (Process Substitution): 명령어 결과를 임시 파일처럼!
명령어의 출력을 파일 이름처럼 다루거나, 반대로 파일처럼 보이는 곳에 쓰면 특정 명령어의 입력으로 들어가게 하는 매우 강력한 기능입니다. 임시 파일을 직접 만들고 지우는 번거로움을 줄여줍니다.
가. <(명령어)
(명령어 출력을 파일처럼 읽기)
명령어
의 표준 출력이 마치 임시 파일에 저장된 것처럼 취급됩니다. 이 "임시 파일"의 이름을 다른 명령어의 인자로 전달하여 읽을 수 있습니다.
- 실습:
# 두 디렉토리의 파일 목록을 비교 # <(ls /bin) 은 /bin 디렉토리 목록을 담고 있는 임시 파일 이름처럼 동작 # <(ls /usr/bin) 은 /usr/bin 디렉토리 목록을 담고 있는 임시 파일 이름처럼 동작 diff <(ls /bin) <(ls /usr/bin) # 여러 명령어의 출력을 하나의 cat 명령어로 합쳐서 보기 cat <(echo "첫 번째 소스: 직접 입력") <(date) <(ls -A /tmp | head -n 2)
나. >(명령어)
(파일처럼 쓰면 명령어 입력으로)
여기에 데이터를 쓰면(리다이렉션하면) 해당 데이터가 명령어
의 표준 입력으로 전달됩니다.
- 실습:
이 예제는
# ls -l 결과를 화면에도 출력하고, 동시에 wc -l 로 라인 수도 세서 line_count.txt 에 저장 # tee 명령어는 표준 입력을 받아 여러 곳(표준 출력, 파일 등)으로 보낼 수 있음 ls -l /etc | tee >(wc -l > line_count.txt) # 위 명령어는 /etc 목록을 화면에 보여주고, 그 라인 수는 line_count.txt 에 저장됩니다. echo "--- /etc 목록의 라인 수 ---" cat line_count.txt
ls -l /etc
의 출력을tee
가 받습니다.tee
는 받은 내용을 표준 출력(화면)으로 보내고, 동시에>(wc -l > line_count.txt)
로도 보냅니다.>(wc -l > line_count.txt)
는wc -l
명령어의 입력이 되는 임시 파일을 의미하며,wc -l
의 결과(라인 수)는line_count.txt
파일에 저장됩니다.
📚 리다이렉션 요약 테이블
연산자 | 설명 | 예시 |
---|---|---|
> |
표준 출력을 파일로 덮어쓰기 | ls > file.txt |
>> |
표준 출력을 파일에 이어쓰기 | date >> file.txt |
< |
파일 내용을 표준 입력으로 사용 | wc -l < file.txt |
<< 구분자 |
Here Document: 여러 줄 입력을 구분자 까지 표준 입력으로 전달 |
cat << EOF ... EOF |
<<< 문자열 |
Here String: 문자열 을 표준 입력으로 전달 |
grep "a" <<< "banana" |
2> |
표준 에러를 파일로 덮어쓰기 | find / -name x 2> err.log |
2>> |
표준 에러를 파일에 이어쓰기 | find / -name y 2>> err.log |
&> 또는 >& |
표준 출력과 표준 에러를 파일로 덮어쓰기 | cmd &> all.log |
&>> |
표준 출력과 표준 에러를 파일에 이어쓰기 (bash 4+) | cmd &>> all.log |
> file 2>&1 |
표준 출력과 표준 에러를 file 로 덮어쓰기 (전통적) |
cmd > all.log 2>&1 |
>> file 2>&1 |
표준 출력과 표준 에러를 file 에 이어쓰기 (전통적) |
cmd >> all.log 2>&1 |
`> | ` | noclobber 옵션 무시하고 강제 덮어쓰기 |
| |
파이프: 명령어1의 출력을 명령어2의 입력으로 | ls | grep .txt |
[n]>&[m] |
파일 디스크립터 n 을 m 으로 리다이렉션 (복제) |
cmd 2>&1 (stderr을 stdout으로) |
[n]<&[m] |
입력 파일 디스크립터 n 을 m 으로 리다이렉션 (복제) |
read line <&3 |
[n]>&- [n]<&- |
파일 디스크립터 n 닫기 |
cmd 2>&- (stderr 닫기) |
<(cmd) |
프로세스 치환: cmd 의 출력을 파일처럼 사용 (입력용) |
diff <(ls /a) <(ls /b) |
>(cmd) |
프로세스 치환: cmd 의 입력을 파일처럼 사용 (출력용) |
ls | tee >(wc -l) |