Поиск по сайту:

Как анализировать данные CSV в Bash


Файлы со значениями, разделенными запятыми (CSV), являются одним из наиболее распространенных форматов для экспортируемых данных. В Linux мы можем читать файлы CSV с помощью команд Bash. Но это может стать очень сложным, очень быстро. Мы протянем руку помощи.

Что такое CSV-файл?

Файл значений, разделенных запятыми, представляет собой текстовый файл, содержащий табличные данные. CSV — это тип данных с разделителями. Как следует из названия, запятая «,» используется для отделения каждого поля данных — или значения — от его соседей.

CSV везде. Если приложение имеет функции импорта и экспорта, оно почти всегда будет поддерживать CSV. Файлы CSV удобочитаемы. Вы можете заглянуть внутрь них с меньшими затратами, открыть их в любом текстовом редакторе и переместить из программы в программу. Например, вы можете экспортировать данные из базы данных SQLite и открыть их в LibreOffice Calc.

Однако даже CSV может стать сложным. Хотите иметь запятую в поле данных? Это поле должно быть заключено в кавычки «\». Чтобы включить кавычки в поле, каждую кавычку необходимо ввести дважды.

Конечно, если вы работаете с CSV, сгенерированным написанной вами программой или сценарием, формат CSV, скорее всего, будет простым и понятным. Если вы вынуждены работать с более сложными форматами CSV, а Linux — это Linux, есть решения, которые мы можем использовать и для этого.

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

Вы можете легко создать образцы данных в формате CSV, используя такие сайты, как Online Data Generator. Вы можете определить нужные поля и выбрать, сколько строк данных вы хотите. Ваши данные генерируются с использованием реалистичных фиктивных значений и загружаются на ваш компьютер.

Мы создали файл, содержащий 50 строк фиктивной информации о сотрудниках:

  • id: простое уникальное целочисленное значение.
  • firstname: имя человека.
  • lastname: фамилия человека.
  • job-title: должность человека.
  • 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 условие разрешается как true, будет выполняться тело цикла while. Тело цикла довольно простое. Набор операторов echo используется для вывода значений некоторых переменных в окно терминала.

Условие цикла while более интересно, чем тело цикла. Мы указываем, что запятая должна использоваться в качестве внутреннего разделителя полей, с оператором IFS=\,\. IFS — это переменная среды. Команда read обращается к своему значению при анализе последовательности текста.

Мы используем параметр -r команды read (сохранять обратную косую черту), чтобы игнорировать любые обратные косые черты, которые могут быть в данных. Они будут рассматриваться как обычные персонажи.

Текст, анализируемый командой 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

  • В первой записи есть запятая в поле job-title, поэтому поле необходимо заключить в кавычки.
  • Во второй записи есть слово, заключенное в два набора кавычек в поле 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 смогут справиться с удобным для Bash CSV.

Инструментарий csvkit

Набор инструментов CSV csvkit – это набор утилит, специально созданных для помощи в работе с CSV-файлами. Вам нужно будет установить его на свой компьютер.

Чтобы установить его в Ubuntu, используйте эту команду:

sudo apt install csvkit

Чтобы установить его в Fedora, вам нужно ввести:

sudo dnf install python3-csvkit

На Манджаро команда:

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 реалистичным и практичным способом, сослужит вам хорошую службу.