Computer >> 컴퓨터 >  >> 프로그래밍 >> Bash 프로그래밍

Bash 스크립트에서 while 루프로 파일을 읽는 다양한 방법

Bash 스크립트에서 파일을 읽는 작업은 프로그래밍에서 매우 흔하게 접하는 연산입니다. 이 글에서는 while 루프를 활용해 파일 내용을 읽는 여러 가지 방법을 살펴봅니다. 하나의 작업도 Bash에서는 다양한 방식으로 수행할 수 있지만, 그중에서도 효율적인 최적의 방법을 아는 것이 중요합니다. 상황에 맞는 기법을 선택할 수 있도록 각 방법의 특징과 동작 원리까지 자세히 설명하겠습니다.

while 루프의 기본 구조

본격적으로 파일을 읽기 전에 while 루프가 어떻게 동작하는지 간단히 짚고 넘어가겠습니다. while 루프는 조건식을 평가하고, 그 조건이 참(true)인 동안 주어진 코드 블록을 반복 실행합니다.

while [ CONDITION ]
do
    code block
done

while 루프 문법을 하나씩 살펴보면 다음과 같습니다.

  • while 루프while 키워드로 시작하며, 뒤에 조건식이 따라옵니다.
  • 조건식[ ] 또는 [[ ]] 안에 감싸야 하며, 루프가 계속 실행되려면 항상 true를 반환해야 합니다.
  • 실제 실행될 코드 블록은 dodone 사이에 위치합니다.
NUMBER=0

while [[ $NUMBER -le 10 ]]
do
    echo " Welcome ${NUMBER} times "
    (( NUMBER++ ))
done

위 예제는 매우 단순한 형태입니다. NUMBER 변수가 10보다 커질 때까지 루프가 반복되며, 매번 echo 문장을 출력합니다.

while 루프와 read 명령어 조합하기

파일 내용을 한 줄씩 읽으려면 whileread 명령어를 함께 사용합니다. 두 명령어를 결합하는 기본 문법은 아래와 같으며, 파일을 입력으로 전달하는 방식에는 여러 가지가 있습니다. 지금부터 그 방법들을 모두 확인해 보겠습니다.

# SYNTAX
while read VARIABLE
do
    code
done

파이프(Pipe) 활용하기

터미널에서 파일 내용을 확인할 때 보통 cat 명령어를 사용합니다. 또한 cat 명령어의 출력을 grep, sort 같은 다른 명령어로 파이프(|)를 통해 전달하기도 하죠.

마찬가지로 여기서도 cat 명령어로 파일 내용을 읽고, 그 결과를 while 루프로 파이핑하는 방식을 사용할 수 있습니다. 설명을 위해 /etc/passwd 파일을 사용하지만, 이 파일은 함부로 수정하는 것이 좋지 않으니 실습이 필요하다면 백업 복사본을 만들어 사용하는 것을 권장합니다.

cat /etc/passwd | while read LREAD
do
    echo ${LREAD}
done

위 코드가 실행될 때 어떤 일이 일어나는지 단계별로 살펴보겠습니다.

  • cat /etc/passwd가 파일 내용을 읽어 파이프를 통해 입력으로 전달합니다.
  • read 명령어는 cat으로부터 전달된 각 줄을 읽어 LREAD 변수에 저장합니다.
  • read 명령어는 파일 끝(EOL)에 도달할 때까지 내용을 계속 읽습니다.

head, tail 같은 다른 명령어의 출력을 while 루프로 파이핑하는 것도 가능합니다.

head -n 5 /etc/passwd | while read LREAD
do
    echo ${LREAD}
done

입력 리디렉션(Input Redirection) 활용하기

입력 리디렉션 연산자 (<)를 사용하면 파일 내용을 while 루프로 직접 전달할 수 있습니다.

while read LREAD
do
    echo ${LREAD}
done < /etc/passwd | head -n 5

파일 이름을 변수에 저장한 뒤 리디렉션 연산자로 넘겨주는 방법도 있습니다.

FILENAME="/etc/passwd"

while read LREAD
do
    echo ${LREAD}
done < ${FILENAME}

또한 파일 이름을 스크립트의 인자(argument)로 받아 처리할 수도 있습니다.

while read LREAD
do
    echo ${LREAD}
done < $1 | head -n 5

내부 필드 구분자(IFS)로 데이터 분리하기

실무에서는 CSV, TXT, JSON 등 다양한 형식의 파일을 다루게 되며, 경우에 따라 사용자 지정 구분자를 기준으로 파일 내용을 나누고 싶을 때가 있습니다. 이럴 때 "내부 필드 구분자(Internal Field Separator, IFS)"를 사용하면 파일 내용을 분리해 각각의 변수에 저장할 수 있습니다.

예를 들어 /etc/passwd 파일은 콜론 (:)을 구분자로 사용합니다. IFS를 활용하면 한 줄의 내용을 필드별로 쪼개 별도의 변수에 담을 수 있습니다.

아래 예제에서는 /etc/passwd 파일을 콜론을 기준으로 분리하여 각 필드를 서로 다른 변수에 저장합니다.

while IFS=":" read A B C D E F G
do
    echo ${A}
    echo ${B}
    echo ${C}
    echo ${D}
    echo ${E}
    echo ${F}
    echo ${G}
done < /etc/passwd

빈 줄(Empty Lines) 처리하기

파일 내용을 루프로 순회할 때 빈 줄은 자동으로 무시되지 않습니다. 이를 확인하기 위해 다음과 같은 샘플 파일을 만들었습니다. 총 4줄의 내용과 몇 개의 빈 줄, 앞뒤 공백, 두 번째 줄의 탭 문자, 그리고 일부 이스케이프 문자(\n, \t)가 포함되어 있습니다.

while read LREAD
do
    echo ${LREAD}
done < testfile

실행 결과를 보면 빈 줄이 무시되지 않고 그대로 출력됩니다. 또 하나 주목할 점은 read 명령어가 앞뒤 공백(whitespace)을 잘라낸다는 사실입니다. 파일을 읽을 때 빈 줄을 건너뛰는 간단한 방법은 문자열 길이가 0인지 검사하는 -z 플래그와 함께 테스트 연산자를 사용하는 것입니다. 같은 예제를 테스트 연산자와 함께 다시 실행해 보겠습니다.

while read LREAD
do
    if [[ ! -z $LREAD ]]
    then
        echo ${LREAD} 
    fi
done < testfile

이번에는 출력 결과에서 빈 줄이 무시된 것을 확인할 수 있습니다.

이스케이프 문자(Escape Characters) 다루기

\n, \t, \c 같은 이스케이프 문자는 파일을 읽을 때 그대로 출력되지 않습니다. 이를 확인하기 위해 이스케이프 문자가 포함된 동일한 샘플 파일을 사용해 보겠습니다.

while read LREAD
do
    echo ${LREAD}
done < testfile

출력 결과를 보면 이스케이프 문자가 본래의 의미를 잃고 \n, \t 대신 nt만 출력된 것을 알 수 있습니다. 역슬래시 해석을 방지하려면 -r 옵션을 사용하면 됩니다.

while read -r LREAD
do
    echo ${LREAD}
done < testfile

마치며

지금까지 Bash 스크립트에서 while 루프로 파일을 읽는 다양한 방법을 살펴봤습니다. 파이프, 입력 리디렉션, IFS를 활용한 필드 분리, 빈 줄 및 이스케이프 문자 처리까지 상황에 맞는 기법을 적절히 활용하면 더욱 견고한 스크립트를 작성할 수 있습니다. 피드백이나 유용한 팁이 있다면 언제든 환영합니다. 여러분의 피드백이 더 좋은 콘텐츠를 만드는 원동력이 됩니다. 즐거운 코딩 되세요!