웹사이트 검색

Bash에서 CSV 데이터를 구문 분석하는 방법


CSV(쉼표로 구분된 값) 파일은 내보낸 데이터의 가장 일반적인 형식 중 하나입니다. Linux에서는 Bash 명령을 사용하여 CSV 파일을 읽을 수 있습니다. 그러나 그것은 매우 복잡하고 빠르게 진행될 수 있습니다. 도와드리겠습니다.

CSV 파일이란?

쉼표로 구분된 값 파일은 표 형식의 데이터를 포함하는 텍스트 파일입니다. CSV는 구분 데이터 유형입니다. 이름에서 알 수 있듯이 쉼표 ','는 데이터의 각 필드(또는 )를 이웃과 구분하는 데 사용됩니다.

CSV는 어디에나 있습니다. 애플리케이션에 가져오기 및 내보내기 기능이 있는 경우 거의 항상 CSV를 지원합니다. CSV 파일은 사람이 읽을 수 있습니다. less로 내부를 살펴보고, 텍스트 편집기에서 열고, 프로그램에서 프로그램으로 이동할 수 있습니다. 예를 들어 SQLite 데이터베이스에서 데이터를 내보내고 LibreOffice Calc에서 열 수 있습니다.

그러나 CSV도 복잡해질 수 있습니다. 데이터 필드에 쉼표를 원하십니까? 해당 필드는 따옴표 \로 둘러싸야 합니다. 필드에 따옴표를 포함하려면 각 따옴표를 두 번 입력해야 합니다.

물론 자신이 작성한 프로그램이나 스크립트에서 생성된 CSV로 작업하는 경우 CSV 형식이 간단하고 직관적일 수 있습니다. 보다 복잡한 CSV 형식으로 작업해야 하는 경우 Linux가 Linux이므로 사용할 수 있는 솔루션도 있습니다.

일부 샘플 데이터

Online Data Generator와 같은 사이트를 사용하여 일부 샘플 CSV 데이터를 쉽게 생성할 수 있습니다. 원하는 필드를 정의하고 원하는 데이터 행 수를 선택할 수 있습니다. 실제 더미 값을 사용하여 데이터가 생성되어 컴퓨터에 다운로드됩니다.

50행의 더미 직원 정보가 포함된 파일을 만들었습니다.

  • id: 단순한 고유 정수 값입니다.
  • 이름: 사람의 이름입니다.
  • : 사람의 성입니다.
  • 직함: 개인의 직함입니다.
  • email-address: 사용자의 이메일 주소입니다.
  • 지사: 근무하는 회사 지사입니다.
  • 상태: 분기가 위치한 상태입니다.

일부 CSV 파일에는 필드 이름을 나열하는 헤더 행이 있습니다. 샘플 파일에는 하나가 있습니다. 파일의 상단은 다음과 같습니다.

첫 번째 행에는 필드 이름이 쉼표로 구분된 값으로 표시됩니다.

파싱 데이터 양식 CSV 파일

CSV 파일을 읽고 각 레코드에서 필드를 추출하는 스크립트를 작성해 보겠습니다. 이 스크립트를 편집기에 복사하고 field.sh라는 파일에 저장합니다.

#! /bin/bash

while IFS="," read -r id firstname lastname jobtitle email branch state
do
  echo "Record ID: $id"
  echo "Firstname: $firstname"
  echo " Lastname: $lastname"
  echo "Job Title: $jobtitle"
  echo "Email add: $email"
  echo " Branch: $branch"
  echo " State: $state"
  echo ""
done < <(tail -n +2 sample.csv)

우리의 작은 스크립트에는 꽤 많은 내용이 포함되어 있습니다. 분해합시다.

우리는 while 루프를 사용하고 있습니다. while 루프 condition이 true로 확인되는 한 while 루프의 본문이 실행됩니다. 루프의 본문은 매우 간단합니다. echo 문의 모음은 일부 변수의 값을 터미널 창에 인쇄하는 데 사용됩니다.

while 루프 조건은 루프 본문보다 더 흥미롭습니다. IFS=\,\ 문과 함께 쉼표를 내부 필드 구분 기호로 사용하도록 지정합니다. IFS는 환경 변수입니다. read 명령은 텍스트 시퀀스를 구문 분석할 때 해당 값을 참조합니다.

데이터에 있을 수 있는 모든 백슬래시를 무시하기 위해 read 명령의 -r(백슬래시 유지) 옵션을 사용하고 있습니다. 일반 문자로 취급됩니다.

read 명령이 구문 분석하는 텍스트는 CSV 필드 이름을 딴 변수 세트에 저장됩니다. field1, field2, ... field7 로 쉽게 이름을 지정할 수 있지만 의미 있는 이름을 사용하면 작업이 더 쉬워집니다.

데이터는 tail 명령의 출력으로 가져옵니다. CSV 파일의 헤더 행을 건너뛸 수 있는 간단한 방법을 제공하기 때문에 tail을 사용하고 있습니다. -n +2(행 번호) 옵션은 tail에게 두 번째 행에서 읽기 시작하도록 지시합니다.

<(...) 구성을 프로세스 대체라고 합니다. Bash가 프로세스의 출력을 마치 파일 디스크립터에서 오는 것처럼 받아들이도록 합니다. 그런 다음 read 명령이 구문 분석할 텍스트를 제공하는 while 루프로 리디렉션됩니다.

chmod 명령을 사용하여 스크립트를 실행 가능하게 만드십시오. 이 문서에서 스크립트를 복사할 때마다 이 작업을 수행해야 합니다. 각 경우에 적절한 스크립트의 이름을 대체하십시오.

chmod +x field.sh

스크립트를 실행하면 레코드가 구성 필드로 올바르게 분할되며 각 필드는 다른 변수에 저장됩니다.

./field.sh

각 레코드는 일련의 필드로 인쇄됩니다.

필드 선택

아마도 우리는 모든 필드를 검색하기를 원하지 않거나 검색할 필요가 없을 것입니다. cut 명령을 통합하여 필드 선택을 얻을 수 있습니다.

이 스크립트를 select.sh라고 합니다.

#!/bin/bash

while IFS="," read -r id jobtitle branch state
do
  echo "Record ID: $id"
  echo "Job Title: $jobtitle"
  echo " Branch: $branch"
  echo " State: $state"
  echo ""
done < <(cut -d "," -f1,4,6,7 sample.csv | tail -n +2)

프로세스 대체 절에 cut 명령을 추가했습니다. -d(구분 기호) 옵션을 사용하여 cut에 쉼표 ,를 구분 기호로 사용하도록 지시합니다. -f(필드) 옵션은 cut에 원하는 필드 1, 4, 6, 7을 알려줍니다. 이 4개의 필드는 while 루프의 본문에 인쇄되는 4개의 변수로 읽혀집니다.

이것은 우리가 스크립트를 실행할 때 얻는 것입니다.

./select.sh

cut 명령을 추가하면 원하는 필드를 선택하고 원하지 않는 필드는 무시할 수 있습니다.

여태까지는 그런대로 잘됐다. 하지만…

처리하는 CSV가 필드 데이터에 쉼표나 따옴표 없이 복잡하지 않은 경우 지금까지 다룬 내용이 CSV 구문 분석 요구 사항을 충족할 것입니다. 발생할 수 있는 문제를 보여주기 위해 데이터의 작은 샘플을 다음과 같이 수정했습니다.

id,firstname,lastname,job-title,email-address,branch,state
1,Rosalyn,Brennan,"Steward, Senior",Rosalyn_Brennan4351@mafthy.com,Minneapolis,Maryland
2,Danny,Redden,"Analyst ""Budget""",Danny_Redden1443@brety.org,Venice,North Carolina
3,Lexi,Roscoe,Pharmacist,,Irlington,Vermont

  • 레코드 1의 직함 필드에 쉼표가 있으므로 필드를 따옴표로 묶어야 합니다.
  • 레코드 2에는 jobs-title 필드에 두 개의 따옴표 세트로 묶인 단어가 있습니다.
  • 레코드 3에는 이메일 주소 필드에 데이터가 없습니다.

이 데이터는 sample2.csv로 저장되었습니다. sample2.csv를 호출하도록 field.sh 스크립트를 수정하고 field2.sh로 저장합니다.

#! /bin/bash

while IFS="," read -r id firstname lastname jobtitle email branch state
do
  echo "Record ID: $id"
  echo "Firstname: $firstname"
  echo " Lastname: $lastname"
  echo "Job Title: $jobtitle"
  echo "Email add: $email"
  echo " Branch: $branch"
  echo " State: $state"
  echo ""
done < <(tail -n +2 sample2.csv)

이 스크립트를 실행하면 간단한 CSV 파서에 크랙이 나타나는 것을 볼 수 있습니다.

./field2.sh

첫 번째 레코드는 직함 필드를 두 개의 필드로 분할하여 두 번째 부분을 이메일 주소로 처리합니다. 이후의 모든 필드는 오른쪽으로 한 칸 이동합니다. 마지막 필드에는 branchstate 값이 모두 포함됩니다.

두 번째 레코드는 모든 따옴표를 유지합니다. Budget이라는 단어 주위에 한 쌍의 인용 부호만 있어야 합니다.

세 번째 레코드는 누락된 필드를 제대로 처리합니다. 이메일 주소가 누락되었지만 다른 모든 것은 원래대로입니다.

반직관적으로 간단한 데이터 형식의 경우 강력한 일반 사례 CSV 파서를 작성하는 것은 매우 어렵습니다. awk와 같은 도구를 사용하면 가까이 갈 수 있지만 항상 간과할 수 있는 극단적인 경우와 예외가 있습니다.

오류가 없는 CSV 파서를 작성하려고 시도하는 것은 아마 최선의 방법이 아닐 것입니다. 특히 어떤 종류의 마감일에 맞춰 작업하는 경우 대안적인 접근 방식은 두 가지 다른 전략을 사용합니다.

하나는 목적에 맞게 설계된 도구를 사용하여 데이터를 조작하고 추출하는 것입니다. 두 번째는 데이터를 삭제하고 포함된 쉼표 및 인용 부호와 같은 문제 시나리오를 대체하는 것입니다. 그러면 간단한 Bash 파서가 Bash 친화적인 CSV에 대처할 수 있습니다.

csvkit 툴킷

CSV 툴킷 csvkit은 CSV 파일 작업을 돕기 위해 명시적으로 생성된 유틸리티 모음입니다. 컴퓨터에 설치해야 합니다.

Ubuntu에 설치하려면 다음 명령을 사용하십시오.

sudo apt install csvkit

Fedora에 설치하려면 다음을 입력해야 합니다.

sudo dnf install python3-csvkit

Manjaro에서 명령은 다음과 같습니다.

sudo pacman -S csvkit

CSV 파일의 이름을 여기에 전달하면 csvlook 유틸리티가 각 필드의 내용을 보여주는 표를 표시합니다. 필드 내용은 CSV 파일에 저장되는 것이 아니라 필드 내용이 나타내는 것을 보여주기 위해 표시됩니다.

문제가 있는 sample2.csv 파일로 csvlook을 시도해 봅시다.

csvlook sample2.csv

모든 필드가 올바르게 표시됩니다. 이것은 문제가 CSV가 아님을 증명합니다. 문제는 스크립트가 너무 단순해서 CSV를 올바르게 해석할 수 없다는 것입니다.

특정 열을 선택하려면 csvcut 명령을 사용합니다. -c(열) 옵션은 필드 이름이나 열 번호 또는 둘 모두와 함께 사용할 수 있습니다.

각 레코드에서 이름과 성, 직함, 이메일 주소를 추출해야 하지만 이름 순서는 성, 이름으로 지정하려고 한다고 가정합니다. 우리가 해야 할 일은 원하는 순서대로 필드 이름이나 번호를 입력하는 것뿐입니다.

이 세 가지 명령은 모두 동일합니다.

csvcut -c lastname,firstname,job-title,email-address sample2.csv
csvcut -c lastname,firstname,4,5 sample2.csv
csvcut -c 3,2,4,5 sample2.csv

csvsort 명령을 추가하여 필드별로 출력을 정렬할 수 있습니다. -c(열) 옵션을 사용하여 정렬할 열을 지정하고 -r(역방향) 옵션을 사용하여 내림차순으로 정렬합니다.

csvcut -c 3,2,4,5 sample2.csv | csvsort -c 1 -r

출력을 더 예쁘게 만들기 위해 csvlook 을 통해 출력할 수 있습니다.

csvcut -c 3,2,4,5 sample2.csv | csvsort -c 1 -r | csvlook

깔끔한 터치는 레코드가 정렬되더라도 필드 이름이 있는 헤더 줄이 첫 번째 줄로 유지된다는 것입니다. 원하는 방식의 데이터를 갖게 된 것에 만족하면 명령 체인에서 csvlook을 제거하고 출력을 파일로 리디렉션하여 새 CSV 파일을 만들 수 있습니다.

sample2.file에 더 많은 데이터를 추가하고 csvsort 명령을 제거하고 sample3.csv라는 새 파일을 만들었습니다.

csvcut -c 3,2,4,5 sample2.csv > sample3.csv

CSV 데이터를 위생 처리하는 안전한 방법

LibreOffice Calc에서 CSV 파일을 열면 각 필드가 셀에 배치됩니다. 찾기 및 바꾸기 기능을 사용하여 쉼표를 검색할 수 있습니다. 사라지도록 nothing으로 바꾸거나 예를 들어 세미콜론 ;와 같이 CSV 구문 분석에 영향을 미치지 않는 문자로 바꿀 수 있습니다.

인용된 필드 주위에 인용 부호가 표시되지 않습니다. 표시되는 유일한 인용 부호는 필드 데이터 내부에 삽입된 인용 부호입니다. 이들은 작은따옴표로 표시됩니다. 이를 찾아 단일 아포스트로피 로 바꾸면 CSV 파일의 큰따옴표가 바뀝니다.

LibreOffice Calc와 같은 응용 프로그램에서 찾기 및 바꾸기를 수행하면 필드 구분 기호 쉼표를 실수로 삭제하거나 인용 필드 주변의 따옴표를 삭제할 수 없습니다. 필드의 데이터 값만 변경합니다.

필드의 모든 쉼표를 세미콜론으로 변경하고 포함된 모든 따옴표를 아포스트로피로 변경하고 변경 사항을 저장했습니다.

그런 다음 sample3.csv를 구문 분석하기 위해 field3.sh라는 스크립트를 만들었습니다.

#! /bin/bash

while IFS="," read -r lastname firstname jobtitle email
do
  echo " Lastname: $lastname"
  echo "Firstname: $firstname"
  echo "Job Title: $jobtitle"
  echo "Email add: $email"
  echo ""
done < <(tail -n +2 sample3.csv)

실행할 때 무엇을 얻는지 봅시다.

./field3.sh

우리의 간단한 파서는 이제 이전에 문제가 있었던 레코드를 처리할 수 있습니다.

많은 CSV를 볼 수 있습니다.

CSV는 틀림없이 응용 프로그램 데이터에 대한 일반적인 언어에 가장 가까운 것입니다. 일부 형태의 데이터를 처리하는 대부분의 애플리케이션은 CSV 가져오기 및 내보내기를 지원합니다. 현실적이고 실용적인 방식으로 CSV를 처리하는 방법을 아는 것이 도움이 될 것입니다.