Ответ 1
Первый прототип с использованием простых старых grep
и cut
:
grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}
Если это достаточно быстро и дает правильный результат, все готово.:)
Я пытаюсь проанализировать CSV, содержащий потенциально 100k + строк. Вот критерии, которые у меня есть:
Я хотел бы получить все строки в CSV, которые имеют заданное значение в данном индексе (ограничено запятыми).
Любые идеи, особенно учитывая производительность?
Первый прототип с использованием простых старых grep
и cut
:
grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}
Если это достаточно быстро и дает правильный результат, все готово.:)
В качестве альтернативы однострочным терминалам, основанным на cut
- или awk
, вы можете использовать специализированный csvtool
aka ocaml-csv
:
$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"
В соответствии с документами он обрабатывает экранирование, цитирование и т.д.
Посмотрите это видео youtube: BASH урок для сценариев 10, работающий с файлами CSV
Файл CSV:
Bob Brown;Manager;16581;Main
Sally Seaforth;Director;4678;HOME
BASH script:
#!/bin/bash
OLDIFS=$IFS
IFS=";"
while read user job uid location
do
echo -e "$user \
======================\n\
Role :\t $job\n\
ID :\t $uid\n\
SITE :\t $location\n"
done < $1
IFS=$OLDIFS
Вывод:
Bob Brown ======================
Role : Manager
ID : 16581
SITE : Main
Sally Seaforth ======================
Role : Director
ID : 4678
SITE : HOME
CSV не так уж и прост. В зависимости от пределов данных, которые у вас есть, вам может понадобиться беспокоиться о цитируемых значениях (которые могут содержать запятые и новые строки) и экранировать кавычки.
Таким образом, если ваши данные достаточно ограничены, вы можете легко скомбинировать с запятой, оболочка script может сделать это легко. Если, с другой стороны, вам необходимо правильно проанализировать CSV, bash не будет моим первым выбором. Вместо этого я бы посмотрел на язык сценариев более высокого уровня, например Python с csv.reader.
В CSV файле каждое поле разделяется запятой. Проблема в том, что само поле может иметь встроенную запятую:
Name,Phone
"Woo, John",425-555-1212
Вам действительно нужен пакет библиотеки, который предлагает надежную поддержку CSV вместо того, чтобы полагаться на использование запятой в качестве разделителя полей. Я знаю, что такие языки сценариев, как Python, имеют такую поддержку. Тем не менее, мне нравится язык сценариев Tcl, поэтому я использую это. Вот простой Tcl script, который делает то, что вы просите:
#!/usr/bin/env tclsh
package require csv
package require Tclx
# Parse the command line parameters
lassign $argv fileName columnNumber expectedValue
# Subtract 1 from columnNumber because Tcl list index starts with a
# zero instead of a one
incr columnNumber -1
for_file line $fileName {
set columns [csv::split $line]
set columnValue [lindex $columns $columnNumber]
if {$columnValue == $expectedValue} {
puts $line
}
}
Сохраните этот script в файл csv.tcl и вызовите его как:
$ tclsh csv.tcl filename indexNumber expectedValue
script считывает файл CSV по строке и сохраняет строку в переменной $line, затем разбивает каждую строку на список столбцов (переменные $столбцы). Затем он выбирает указанный столбец и присваивает его переменной $columnValue. Если есть совпадение, распечатайте исходную строку.
Использование awk
:
export INDEX=2
export VALUE=bar
awk -F, '$'$INDEX' ~ /^'$VALUE'$/ {print}' inputfile.csv
Изменить: В соответствии с отличным комментарием Денниса Уильямсона, это может быть гораздо более чисто (и безопасно) написано путем определения awk-переменных с использованием -v
:
awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' inputfile.csv
Jeez... с переменными и всем, awk почти реальный язык программирования...
index=1
value=2
awk -F"," -v i=$index -v v=$value '$(i)==v' file
В ситуациях, когда данные не содержат каких-либо специальных символов, решение, предложенное Nate Kohl и ghostdog74, является хорошим.
Если данные содержат запятые или новые строки внутри полей, awk может неправильно подсчитывать номера полей, и вы получите неправильные результаты.
Вы все еще можете использовать awk с некоторой помощью из программы, которую я написал, называемой csvquote (доступной в https://github.com/dbro/csvquote):
csvquote inputfile.csv | awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' | csvquote -u
Эта программа находит специальные символы внутри указанных полей и временно заменяет их непечатаемыми символами, которые не будут путать awk. Затем они восстанавливаются после завершения awk.
A sed
или awk
решение, вероятно, будет короче, но здесь для Perl:
perl -F/,/ -ane 'print if $F[<INDEX>] eq "<VALUE>"`
где <INDEX>
основано на 0 (0 для первого столбца, 1 для второго столбца и т.д.)
Я искал элегантное решение, поддерживающее цитирование, и не требовал установки каких-либо моментов в моем устройстве VMware vMA. Оказывается, этот простой python script делает трюк! (Я назвал script csv2tsv.py
, так как он преобразует CSV в значения, разделенные табуляцией - TSV)
#!/usr/bin/env python
import sys, csv
with sys.stdin as f:
reader = csv.reader(f)
for row in reader:
for col in row:
print col+'\t',
print
Значения, разделенные табуляцией, могут быть легко разделены с помощью команды вырезания (не нужно указывать разделитель, вкладка по умолчанию). Здесь пример использования/вывода:
> esxcli -h $VI_HOST --formatter=csv network vswitch standard list |csv2tsv.py|cut -f12
Uplinks
vmnic4,vmnic0,
vmnic5,vmnic1,
vmnic6,vmnic2,
В моих сценариях я фактически собираюсь анализировать вывод tsv по строкам и использовать чтение или вырезание, чтобы получить нужные мне поля.