Profile picture

[Linux] 리다이렉션 정리하기

JaehyoJJAng2023년 04월 24일

💻 리눅스 리다이렉션 완벽 정복: 기본부터 고급까지 (실습 예제 포함)

안녕하세요! 리눅스 환경에서 터미널 작업을 하다 보면 명령어의 결과를 파일로 저장하거나, 파일의 내용을 명령어의 입력으로 사용해야 하는 경우가 정말 많습니다. 이때 마법처럼 등장하는 것이 바로 **리다이렉션(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").
  • 실습:

    1. 기본 사용 (변수 및 명령어 치환):

      MY_EDITOR="VSCode"
      cat << EOF
      안녕하세요! 제가 주로 사용하는 편집기는 $MY_EDITOR 입니다.
      오늘은 $(date '+%Y년 %m월 %d일 %A') 입니다.
      이 내용은 Here Document를 통해 전달되었습니다.
      EOF
      # 예상 출력:
      # 안녕하세요! 제가 주로 사용하는 편집기는 VSCode 입니다.
      # 오늘은 2025년 05월 09일 금요일 입니다.
      # 이 내용은 Here Document를 통해 전달되었습니다.
    2. 파일에 저장하기:

      cat << EOF > my_config.txt
      # 이것은 설정 파일 예시입니다.
      USER_NAME="guest"
      TIMEOUT=30
      ENABLE_FEATURE_X=true
      EOF
      
      echo "--- my_config.txt 파일 내용 ---"
      cat my_config.txt
    3. 변수 치환 방지:

      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의 복사본으로 만듭니다. 즉, nm과 같은 곳을 가리키도록 합니다. 예를 들어, 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] 파일 디스크립터 nm으로 리다이렉션 (복제) cmd 2>&1 (stderr을 stdout으로)
[n]<&[m] 입력 파일 디스크립터 nm으로 리다이렉션 (복제) read line <&3
[n]>&- [n]<&- 파일 디스크립터 n 닫기 cmd 2>&- (stderr 닫기)
<(cmd) 프로세스 치환: cmd의 출력을 파일처럼 사용 (입력용) diff <(ls /a) <(ls /b)
>(cmd) 프로세스 치환: cmd의 입력을 파일처럼 사용 (출력용) ls | tee >(wc -l)
    Tag -

Loading script...