Скрипты оболочки Bash - синтаксический анализ CSV - PullRequest
33 голосов
/ 13 октября 2009

Я пытаюсь проанализировать CSV, содержащий потенциально более 100 000 строк. Вот критерии, которые у меня есть:

  1. Индекс идентификатора
  2. Значение идентификатора

Я хотел бы получить все строки в CSV, которые имеют заданное значение в указанном индексе (разделенные запятыми).

Какие-нибудь идеи, с особым вниманием к производительности?

Ответы [ 10 ]

42 голосов
/ 13 октября 2009

В качестве альтернативы однострочникам на основе cut - или awk можно использовать специализированные csvtool aka ocaml-csv:

$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"

Согласно документации, он обрабатывает экранирование, цитирование и т. Д.

39 голосов
/ 16 апреля 2013

Смотрите это видео на YouTube: Урок 10 сценариев BASH по работе с файлами CSV

CSV-файл:

Bob Brown;Manager;16581;Main
Sally Seaforth;Director;4678;HOME

Скрипт Bash:

#!/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
26 голосов
/ 13 октября 2009

Первый прототип с использованием простого старого grep и cut:

grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}

Если это достаточно быстро и дает правильный результат, все готово. :)

11 голосов
/ 13 октября 2009

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

Так что, если ваши данные достаточно ограничены, можно просто обойтись без простого разделения через запятую, сценарий оболочки может сделать это легко. С другой стороны, если вам нужно «правильно» проанализировать CSV, bash не будет моим первым выбором. Вместо этого я бы посмотрел на язык сценариев более высокого уровня, например Python с csv.reader .

7 голосов
/ 13 октября 2009

В CSV-файле каждое поле отделяется запятой. Проблема в том, что само поле может иметь запятую:

Name,Phone
"Woo, John",425-555-1212

Вам действительно нужен библиотечный пакет, который предлагает надежную поддержку CSV вместо того, чтобы полагаться на использование запятой в качестве разделителя полей. Я знаю, что языки сценариев, такие как Python, имеют такую ​​поддержку. Тем не менее, я чувствую себя комфортно с языком сценариев Tcl, так что это то, что я использую. Вот простой Tcl-скрипт, который делает то, что вы просите:

#!/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's 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
    }   
}

Сохраните этот скрипт в файл с именем csv.tcl и вызовите его как:

$ tclsh csv.tcl filename indexNumber expectedValue

Объяснение

Скрипт построчно читает файл CSV и сохраняет строку в переменной $ line, затем разбивает каждую строку на список столбцов (переменная $ columns). Затем он выбирает указанный столбец и присваивает его переменной $ columnValue. Если есть совпадение, распечатайте оригинальную строку.

7 голосов
/ 13 октября 2009

Использование 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

Боже ... с переменными и всем остальным, awk - это почти настоящий язык программирования ...

4 голосов
/ 05 мая 2013

Для ситуаций, когда данные не содержат специальных символов, решение, предложенное Нейтом Колем и 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.

3 голосов
/ 13 октября 2009
index=1
value=2
awk -F"," -v i=$index -v v=$value '$(i)==v' file
1 голос
/ 03 апреля 2014

Я искал элегантное решение, которое бы поддерживало цитирование и не требовало установки чего-либо необычного на моем устройстве VMware vMA. Оказывается, этот простой скрипт на Python делает свое дело! (Я назвал скрипт 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 построчно и использовать read или cut, чтобы получить нужные мне поля.

1 голос
/ 13 октября 2009

A sed или awk решение, вероятно, будет короче, но вот для Perl:

perl -F/,/ -ane 'print if $F[<INDEX>] eq "<VALUE>"`

, где <INDEX> - это 0 (0 для первого столбца, 1 для 2-го столбца и т. Д.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...