Как извлечь один столбец из файла csv



Если у меня есть csv-файл, есть ли быстрый способ bash распечатать содержимое только одного столбца? Можно с уверенностью предположить, что каждая строка имеет одинаковое количество столбцов, но содержание каждого столбца будут иметь разную длину.

858   12  

12 ответов:

вы можете использовать awk, для этого. Измените "$2 " на N-й столбец, который вы хотите.

awk -F "\"*,\"*" '{print }' textfile.csv

да. cat mycsv.csv | cut -d ',' -f3 напечатает 3-й столбец.

самый простой способ, которым я смог это сделать, был просто использовать csvtool. У меня были и другие варианты использования, чтобы использовать csvtool, и он может обрабатывать кавычки или разделители соответствующим образом, если они появляются в самих данных столбца.

csvtool format '%(2)\n' input.csv

замена 2 на номер столбца эффективно извлекает данные столбца, которые вы ищете.

приземлился здесь, чтобы извлечь из файла, разделенного вкладкой. Думал, что я добавлю.

cat textfile.tsv | cut -f2 -s

здесь -f2 извлекает 2, ненулевой индексированный столбец или второй столбец.

другие ответы работают хорошо, но так как вы попросили решение, используя только оболочку bash, вы можете сделать это:

AirBoxOmega:~ d$ cat > file #First we'll create a basic CSV
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10

и тогда вы можете вытащить столбцы (первый в этом примере) вот так:

AirBoxOmega:~ d$ while IFS=, read -a csv_line;do echo "${csv_line[0]}";done < file
a
1
a
1
a
1
a
1
a
1
a
1

Итак, здесь происходит несколько вещей:

  • while IFS=, - это говорит, чтобы использовать запятую в качестве IFS (внутренний разделитель полей), который является то, что оболочка использует, чтобы знать, что разделяет поля (блоки текста). Так сказать, если=, как сказав "А,Б" такой же как "б" если бы "если" =" " (каким он и является по умолчанию.)

  • read -a csv_line; - это говорит читать в каждой строке, по одному за раз и создать массив, где каждый элемент называется "csv_line" и отправить его в раздел " do " нашего цикла while

  • do echo "${csv_line[0]}";done < file - теперь мы находимся в фазе "do", и мы говорим Эхо 0-й элемент массива"csv_line". Это действие повторяется в каждой строке файла. Этот < file часть просто говорю, а цикл, в котором, чтобы прочитать от. Примечание: помните, что в bash массивы индексируются 0, поэтому первый столбец является 0-м элементом.

Итак, у вас есть это, вытаскивая столбец из CSV в оболочке. Другие решения, вероятно, более практичны, но это чистый bash.

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

FS (разделитель полей) - переменная, значение которой равно dafaulted пространство. Поэтому awk по умолчанию разбивается на пробел для любой строки.

таким образом, используя BEGIN (Execute before taking input) мы можем установить это поле для всего, что мы хотим...

awk 'BEGIN {FS = ","}; {print }'

приведенный выше код будет печатать 3-й столбец в CSV-файл.

[dumb@one pts]$ cat > file #Сначала мы создадим базовый CSV
a, b,c,d,e,f,g,h,i, k
1,2,3,4,5,6,7,8,9,10
a, b,c,d,e,f,g,h,i, k
1,2,3,4,5,6,7,8,9,10

[dumb@one pts]$ awk-F , '{print $1}' file
а
1
а
1

вы можете использовать GNU Awk, см. эта статья руководства пользователя. В качестве улучшения решения, представленного в статье (в июне 2015 года), следующая команда gawk разрешает двойные кавычки внутри полей с двойными кавычками; двойная кавычка помечается двумя последовательными двойными кавычками ("") там. Кроме того, это позволяет пустые поля,но даже это не может обрабатывать многострочные поля. В следующем примере печатается 3-й столбец (через c=3) из текстовый файл.csv:

#!/bin/bash
gawk -- '
BEGIN{
    FPAT="([^,\"]*)|(\"((\"\")*[^\"]*)*\")"
}
{
    if (substr($c, 1, 1) == "\"") {
        $c = substr($c, 2, length($c) - 2) # Get the text within the two quotes
        gsub("\"\"", "\"", $c)  # Normalize double quotes
    }
    print $c
}
' c=3 < <(dos2unix <textfile.csv)

обратите внимание на использование dos2unix для преобразования возможных разрывов строк стиля DOS (CRLF т. е. "\r\n") и кодировки UTF-16 (с меткой порядка байтов) в "\n" и UTF-8 (без метки порядка байтов) соответственно. Стандартные CSV-файлы используют CRLF в качестве разрыва строки, см. Википедия.

если входные данные могут содержать многострочные поля, можно использовать следующий скрипт. обратите внимание на использование специальной строки для разделения записей в выводе (так как новая строка разделителя по умолчанию может возникать в записи). Опять же, в следующем примере печатается 3-й столбец (через c=3) из текстового файла.csv:

#!/bin/bash
gawk -- '
BEGIN{
    RS="" # Read the whole input file as one record;
    # assume there is no null character in input.
    FS="" # Suppose this setting eases internal splitting work.
    ORS="\n####\n" # Use a special output separator to show borders of a record.
}
{
    nof=patsplit(, a, /([^,"\n]*)|("(("")*[^"]*)*")/, seps)
    field=0;
    for (i=1; i<=nof; i++){
        field++
        if (field==c) {
            if (substr(a[i], 1, 1) == "\"") {
                a[i] = substr(a[i], 2, length(a[i]) - 2) # Get the text within 
                # the two quotes.
                gsub(/""/, "\"", a[i])  # Normalize double quotes.
            }
            print a[i]
        }
        if (seps[i]!=",") field=0
    }
}
' c=3 < <(dos2unix <textfile.csv)

есть и другой подход к проблеме. csvquote может выводить содержимое файла CSV, измененного таким образом, чтобы специальные символы в поле были преобразованы так, чтобы обычные инструменты обработки текста Unix можно было использовать для выбора определенного столбца. Например, следующий код выводит третий колонка:

csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u

csvquote может использоваться для обработки произвольных больших файлов.

мне нужен правильный CSV-разбор, а не cut/awk и молитва. Я пытаюсь это на mac без csvtool, но macs поставляются с ruby, так что вы можете сделать:

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby

вы не можете сделать это без полного парсера CSV.

некоторое время использовал этот код, он не "быстрый", если вы не считаете "вырезание и вставка из stackoverflow".

он использует операторы ${##} и ${%%} в цикле вместо IFS. Он называет " err " и " die " и поддерживает только запятую, тире и трубу в качестве символов SEP (это все, что мне нужно).

err()  { echo "${0##*/}: Error:" "$@" >&2; }
die()  { err "$@"; exit 1; }

# Return Nth field in a csv string, fields numbered starting with 1
csv_fldN() { fldN , "" ""; }

# Return Nth field in string of fields separated
# by SEP, fields numbered starting with 1
fldN() {
        local me="fldN: "
        local sep=""
        local fldnum=""
        local vals=""
        case "$sep" in
                -|,|\|) ;;
                *) die "$me: arg1 sep: unsupported separator '$sep'" ;;
        esac
        case "$fldnum" in
                [0-9]*) [ "$fldnum" -gt 0 ] || { err "$me: arg2 fldnum=$fldnum must be number greater or equal to 0."; return 1; } ;;
                *) { err "$me: arg2 fldnum=$fldnum must be number"; return 1;} ;;
        esac
        [ -z "$vals" ] && err "$me: missing arg2 vals: list of '$sep' separated values" && return 1
        fldnum=$(($fldnum - 1))
        while [ $fldnum -gt 0 ] ; do
                vals="${vals#*$sep}"
                fldnum=$(($fldnum - 1))
        done
        echo ${vals%%$sep*}
}

пример:

$ CSVLINE="example,fields with whitespace,field3"
$ $ for fno in $(seq 3); do echo field$fno: $(csv_fldN $fno "$CSVLINE");  done
field1: example
field2: fields with whitespace
field3: field3
csvtool col 2 file.csv 

где 2-столбец, который вас интересует

вы также можете сделать

csvtool col 1,2 file.csv 

сделать несколько столбцов

Comments

    Ничего не найдено.