Как разобрать CSV-файл в Bash?

112

Я работаю над длинным скриптом на Bash. Я хочу читать ячейки из файла CSV в переменные Bash. Я могу разбирать строки и первый столбец, но не любой другой столбец. Вот мой код:


  cat myfile.csv|while read line
  do
    read -d, col1 col2 < <(echo $line)
    echo "I got:$col1|$col2"
  done

Он печатает только первый столбец. В качестве дополнительного теста я попробовал следующее:

read -d, x y < <(echo a,b,)

И $ y пусто. Итак, я попробовал:

read x y < <(echo a b)

И $ y есть b. Зачем?

Пользователь1
источник
7
Вы рассмотрели awkиспользование $1, $2и т.д.?
BeemerGuy
4
в качестве примечания: command <<(echo "string") ---> command <<< "string"
tokland
1
Для этого была разработана программа командной строки 'cut': ss64.com/bash/cut.html
Джей

Ответы:

216

Вам нужно использовать IFSвместо -d:

while IFS=, read -r col1 col2
do
    echo "I got:$col1|$col2"
done < myfile.csv

Обратите внимание, что для общего анализа CSV вы должны использовать специализированный инструмент, который может обрабатывать поля в кавычках с внутренними запятыми, среди других проблем, которые Bash не может решить самостоятельно. Примеры таких инструментов: cvstoolи csvkit.

Приостановлено до дальнейшего уведомления.
источник
7
Предлагаемое решение подходит для очень простых файлов CSV, то есть если заголовки и значения не содержат запятых и встроенных кавычек. На самом деле довольно сложно написать общий анализатор CSV (тем более, что существует несколько «стандартов» CSV). Один из подходов к тому, чтобы сделать файлы CSV более доступными для инструментов * nix, - это преобразовать их в TSV (значения, разделенные табуляцией), например, с помощью Excel.
пик
Интересно, что mkdir в теле сделать не могу. Я получаю command not found. Только echoработает.
Zsolt
1
@Zsolt: Нет никаких причин, по которым это должно быть. У вас должна быть опечатка или случайный непечатаемый символ.
Приостановлено до дальнейшего уведомления.
2
@DennisWilliamson Вы должны заключить разделитель, например, при использовании ;:while IFS=";" read col1 col2; do ...
thomas.mc.work
1
@ thomas.mc.work: Это верно в случае точек с запятой и других символов, которые являются специальными для оболочки. В случае запятой в этом нет необходимости, и я предпочитаю опускать ненужные символы. Например, вы всегда можете указать переменные для раскрытия, используя фигурные скобки (например ${var}), но я опускаю их, когда они не нужны. Мне это кажется чище.
Приостановлено до дальнейшего уведомления.
10

Со manстраницы:

-d delim Первый символ delim используется для завершения строки ввода, а не новой строки.

Вы используете, -d,который завершит строку ввода запятой. Он не будет читать остальную часть строки. Вот почему $ y пусто.

кендырь
источник
3

Мы можем анализировать CSV-файлы с помощью строк в кавычках и разделенных с помощью say | со следующим кодом

while read -r line
do
    field1=$(echo $line | awk -F'|' '{printf "%s", $1}' | tr -d '"')
    field2=$(echo $line | awk -F'|' '{printf "%s", $2}' | tr -d '"')

    echo $field1 $field2
done < $csvFile

awk преобразует строковые поля в переменные, а tr удаляет кавычки.

Немного медленнее, так как awk выполняется для каждого поля.

Майтхилиш
источник
1
Хорошо, вы также можете использовать кому (,)
pkarc
0

Если вы хотите прочитать CSV-файл с несколькими строками, это решение.

while IFS=, read -ra line
do 
    test $i -eq 1 && ((i=i+1)) && continue
    for col_val in ${line[@]}
    do
        echo -n "$col_val|"                 
    done
    echo        
done < "$csvFile"
Элия
источник