텍스트처리_06 CSV 파일에 ID 목록을 입력해서 대응하는 ID 컬럼값 얻기

 
  • 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어

명령어: read, grep
키워드: IFS, CSV, 레코드, 컬럼, 구분자, 분할
사용처: ID 목록 파일과 CSV 파일에서 지정한 ID의 컬럼값을 표시하고 싶을 때


실행예제

$ cat data.csv  <------------ CSV 파일 확인
0001,Kim,45
0002,Lee,312
0003,Park,102
0004,Kang,3
0005,Seo,92

$ cat id.lst  <-------------- 입력 ID 파일 확인
0003
0004

$ ./csv-list.sh id.lst  <---- ID 목록과 일치하는 이름 표시
Park
Kang

스크립트

#!/bin/sh

filecheck()
{
    if [ ! -f "$1" ]; then
        echo "ERROR: File $1 does not exist." >&2
        exit 1
    fi
}

# CSV 파일명과 ID 목록 파일명으르 지정해서 파일 존재 확인
csvfile="data.csv"
idlistfile="$1"

filecheck "$csvfile"
filecheck "$idlistfile"

while IFS=, read id name score
do
    grep -xq "$id" "$idlistfile"
    if [ $? -eq 0 ]; then
        echo $name
    fi
done < "$csvfile"

   

해설

이 스크립트는 ID 목록 파일을 명령행 인수로 지정해서 CSV 파일에서 일치하는 ID 컬럼값을 취득합니다. 대상 CSV 파일은 다음처럼 “ID번호, 이름, 점수” 형식이라고 가정합니다.

  • 파일1 CSV 파일(data.csv)
<data.csv 파일 내용>
0001,Kim,45
0002,Lee,312
0003,Park,102
0004,Kang,3
0005,Seo,92

ID 목록 파일은 추출하고 싶은 ID가 적힌 텍스트 파일입니다.

  • 파일2 추출하고 싶은 ID 목록 파일(id.lst)
<id.lst> 파일 내용
0003
0004

예제에서는 IFS를 ,(쉼표)로 설정해서 read 명령어로 CSV 파일을 다룹니다.

1에서 셸 변수 csvfile에 CSV 파일명을 설정하고, 셸 변수 idlistfile에 ID 목록 파일명을 설정해서 파일 존재를 확인합니다. 존재 확인은 셸 함수 filecheck()로 처리합니다.

셸 변수 filecheck()는 test 명령어 -f 연산자(대상이 일반 파일인지 확인)를 사용해서 대상 파일을 확인합니다2. 부정 연산자 !를 사용해서 대상이 디렉터리이거나 파일이 존재하지 않으면 에러를 표시하고 종료합니다.

3에서 CSV 파일에서 셸 변수 id, name, score에 대응하는 값을 읽습니다. 이 처리를 위해 임시로 환경 변수를 설정하고 명령어를 실행하는 방법을 설명합니다. 예제에서는 일시적으로 설정하는 환경 변수로 IFS를 사용합니다.

  • 일시적으로 환경 변수를 설정해서 명령어를 실행하는 예
    $ TMPDIR=/mytmp ./start.sh
    

환경 변수 TMPDIR을 임시로 설정해서 start.sh를 실행합니다. 이 줄 다음부터 환경 변수 TMPDIR에 설정한 값은 원래 설정한 값이 되어서 /mytmp가 아니게 됩니다. 즉, “환경 변수=값 명령어”는 어떤 특정 명령어나 스크립트를 실행할 때만 일시적으로 환경 변수를 설정하게 됩니다.

3while문 조건식은 다음과 같습니다.

IFS=, read id name score

즉, 임시로 환경 변수 IFS를 ,(쉼표)로 설정해 read 명령어를 실행합니다. 여기서 id, name, score는 셸 변수입니다.

환경 변수 IFS에 쉼표를 임시로 설정하면 셸이 해석하는 구분자를 쉼표로만 지정할 수 있습니다. 이러면 쉼표로 나뉜 줄을 분해해서 각각 셸 변수에 대입하게 됩니다.

3에서 지정한 while문을 정리하면 “셸 변수 csvfile로 지정한 CSV 파일에서 한 줄씩 읽어서 read 명령어를 사용해서 셸 변수에 값을 대입합니다. 이때 값의 구분자는 환경 변수 IFS에 ,(쉼표)를 설정해서 쉼표로 구분합니다. 이렇게 하면 쉼표로 줄을 분할해서 각각의 컬럼값이 셸 변수 id, name, score에 대입된다.”라는 뜻이 됩니다. 조금 복잡하지만 이런 방법은 CSV 파일을 다룰 때 자주 사용하는 방법이므로 잘 이해하기 바랍니다.

4에서 ID 목록 파일의 ID와 CSV 파일의 ID가 일치하는지 확인하기 위해 grep 명령어의 -x 옵션과 -q 옵션을 사용합니다. grep 명령어 -x 옵션은 한 줄 전체가 패턴과 완전 일치할 때만 선택하는 옵션입니다. grep으로 “0001”이라는 ID로 검색하면 “00010”이라는 ID도 찾아서 파일에서 ID를 검색할 때 잘못된 결과를 출력할지도 모릅니다. CSV 파일에서 추출한 ID가 ID 목록 파일과 완전 일치하는지 확인하기 위해 -x 옵션을 사용하는 것입니다.

또한 4에서 일치하는지 여부만 종료 스테이터스로 확인하므로 grep 명령어 검색 결과를 출력하지 않는 -q 옵션을 사용합니다.

5에서 grep 명령어 종료 스테이터스를 판단해서 일치 여부를 확인합니다. 일치하면 종료 스테이터스 $?가 0이 되므로 if문에서 판정해서 참일 때 CSV 파일에서 추출한 이름(셸 변수 name)을 echo 명령어로 표시하면 해당 ID의 이름을 추출할 수 있습니다.

   

주의사항

  • 값 자체에 쉼표가 포함된 파일은 이 스크립트에서 다룰 수 없습니다.

  • 예제처럼 이름만 추출해서 표시하는게 아니라 단순히 ID와 일치하는 줄 전체를 표시하고 싶을 때 아래와 같이 grep 명령어의 -f 옵션을 사용합니다.

    • ID목록을 -f 옵션으로 지정
      $ grep -f id.lst data.csv
      0003,Park,102
      0004,Kang,3
      

      하지만 이 예제에서는 id.lst 파일에 [0001]이 있으면 [00010]이라는 ID와 일치하게 됩니다. 또한, 만약 이름이 [0001 Song]인 경우에도 일치하게 됩니다. 이럴 때 ID 목록 파일 첫 글자에 ^를 붙이고 ID 끝에 ,를 붙인 [^0001,]라고 적으면 이런 문제를 피할 수 있습니다.