Како да ги анализирате податоците од CSV во Bash


Датотеките со разделени вредности со запирки (CSV) се еден од најчестите формати за извезени податоци. На Linux, можеме да читаме CSV датотеки користејќи Bash команди. Но, може да стане многу комплицирано, многу брзо. Ќе подадеме рака.

Што е CSV-датотека?

Датотеката со вредности разделени со запирки е текстуална датотека што содржи табеларни податоци. CSV е тип на разграничени податоци. Како што сугерира името, запирката „,“ се користи за да се оддели секое поле со податоци — или вредност — од неговите соседи.

CSV е насекаде. Ако апликацијата има функции за увоз и извоз, таа скоро секогаш ќе поддржува CSV. CSV-датотеките се читливи од човек. Можете да погледнете внатре во нив со помалку, да ги отворите во кој било уредувач на текст и да ги преместувате од програма до програма. На пример, можете да ги извезете податоците од базата на податоци на SQLite и да ја отворите во LibreOffice Calc.

Сепак, дури и CSV може да стане комплициран. Сакате да имате запирка во полето за податоци? Тоа поле треба да има наводници „\“ обвиткани околу него. За да вклучи наводници во полето, секој наводник треба да се внесува двапати.

Се разбира, ако работите со CSV генериран од програма или скрипта што сте ја напишале, форматот CSV најверојатно ќе биде едноставен и јасен. Ако сте принудени да работите со посложени CSV формати, при што Linux е Linux, постојат решенија што можеме да ги користиме и за тоа.

Некои примероци на податоци

Можете лесно да генерирате некои примероци од CSV податоци, користејќи сајтови како што е генератор на онлајн податоци. Можете да ги дефинирате полињата што ги сакате и да изберете колку редови на податоци сакате. Вашите податоци се генерираат со реални лажни вредности и се преземаат на вашиот компјутер.

Создадовме датотека која содржи 50 редови на лажни информации за вработените:

  • id: едноставна единствена цел број вредност.
  • име: името на личноста.
  • презиме: презимето на лицето.
  • работно звање: Називот на работното место на лицето.
  • адреса на е-пошта: адресата на е-пошта на лицето.
  • филијала: Филијалата на компанијата во која работат.
  • состојба: состојбата во која се наоѓа филијалата.

Некои 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 состојбата се реши во точно, телото на циклусот while ќе се изврши. Телото на јамката е прилично едноставно. Збирка на изјави echo се користат за печатење на вредностите на некои променливи во терминалниот прозорец.

Состојбата на јамката while е поинтересна од телото на јамката. Назначуваме дека треба да се користи запирка како разделувач на внатрешно поле, со исказот IFS=\,\. IFS е променлива на околината. Командата читај се однесува на нејзината вредност при парсирање на секвенци од текст.

Ја користиме опцијата -r (задржи назад) на командата читај за да ги игнорираме сите задни црти што може да ги има во податоците. Тие ќе се третираат како редовни ликови.

Текстот што командата read го анализира е зачуван во збир на променливи именувани по полињата CSV. Тие исто толку лесно можеа да бидат именувани field1, field2, ... field7, но значајните имиња го олеснуваат животот.

Податоците се добиваат како излез од командата tail. Користиме tail бидејќи ни дава едноставен начин да ја прескокнеме линијата за заглавие на датотеката CSV. Опцијата -n +2 (број на линија) му кажува на tail да почне да чита од линијата број два.

Конструкцијата <(...) се нарекува замена на процесот. Тоа предизвикува Bash да го прифати излезот од процесот како да доаѓа од дескриптор на датотека. Ова потоа се пренасочува во циклусот while, обезбедувајќи го текстот што ќе го анализира командата read.

Направете ја скриптата извршна со помош на командата 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 дека сакаме полиња едно, четири, шест и седум. Тие четири полиња се читаат во четири променливи, кои се печатат во телото на циклусот while.

Ова е она што го добиваме кога го извршуваме сценариото.

./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

  • Записот има запирка во полето работна титула, така што полето треба да се завитка во наводници.
  • Записот два има збор завиткан во две групи наводници во полето jobs-title.
  • Записот три нема податоци во полето email-address.

Овие податоци се зачувани како „sample2.csv“. Изменете ја вашата скрипта „field.sh“ за да го повикате „sample2.csv“ и зачувајте ја како „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

Првиот запис го дели полето за наслов на работа на две полиња, третирајќи го вториот дел како адреса на е-пошта. Секое поле после ова е поместено за едно место надесно. Последното поле ги содржи и вредностите гранка и state.

Вториот запис ги задржува сите наводници. Треба да има само еден пар наводници околу зборот „Буџет“.

Третиот запис всушност се справува со полето што недостасува како што треба. Недостасува адресата на е-пошта, но се останато е како што треба.

Контраинтуитивно, за едноставен формат на податоци, многу е тешко да се напише робустен CSV парсер со општ случај. Алатките како awk ќе ви овозможат да се приближите, но секогаш има рабови и исклучоци што се провлекуваат.

Обидот да се напише непогрешлив CSV парсер веројатно не е најдобриот начин напред. Алтернативен пристап - особено ако работите до некој вид рок - користи две различни стратегии.

Една од нив е да користите алатка дизајнирана наменски за манипулирање и извлекување на вашите податоци. Втората е да ги санирате вашите податоци и да ги замените проблематичните сценарија како што се вградените запирки и наводниците. Вашите едноставни Bash анализатори потоа можат да се справат со CSV-пријателскиот за Bash.

Комплет со алатки csvkit

Комплетот со алатки CSV csvkit е збирка на алатки изрично создадени за да помогнат во работата со CSV-датотеките. Ќе треба да го инсталирате на вашиот компјутер.

За да го инсталирате на Ubuntu, користете ја оваа команда:

sudo apt install csvkit

За да го инсталирате на Fedora, треба да напишете:

sudo dnf install python3-csvkit

На Manjaro командата е:

sudo pacman -S csvkit

Ако ѝ го пренесеме името на датотеката CSV, алатката csvlook  прикажува табела што ја прикажува содржината на секое поле. Содржината на полето се прикажува за да покаже што претставува содржината на полето, а не како што е зачувана во датотеката CSV.

Ајде да се обидеме со csvlook со нашата проблематична датотека „sample2.csv“.

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

Ако отворите CSV-датотека во LibreOffice Calc, секое поле ќе биде сместено во ќелија. Можете да ја користите функцијата за наоѓање и замена за да барате запирки. Може да ги замените со „ништо“ за да исчезнат или со знак што нема да влијае на парсирањето на CSV, како на пример полузапирка „;“.

Нема да ги видите наводниците околу цитираните полиња. Единствените наводници што ќе ги видите се вградените наводници внатре податоците од полето. Тие се прикажани како единечни наводници. Наоѓањето и заменувањето на нив со еден апостроф „“ ќе ги замени двојните наводници во датотеката CSV.

Пронаоѓањето и заменувањето во апликација како LibreOffice Calc значи дека не можете случајно да избришете некоја од запирките за раздвојување на полињата, ниту да ги избришете наводниците околу цитираните полиња. Ќе ги менувате само вредностите на податоците на полињата.

Ги сменивме сите запирки во полињата со точка-запирка и сите вградени наводници со апострофи и ги зачувавме нашите промени.

Потоа создадовме скрипта наречена „field3.sh“ за да го анализираме „sample3.csv“.

#! /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 - на реален и практичен начин - ќе ве застане на добро место.