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


111

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

Ответы:


137

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

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

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'будет печатать 2вместо 2,3,4,5.
Игорь Микушкин

Если вам повезло, и вы используете GNU Tools в Windows, вы можете выполнить ту же команду, что и @IgorMikushkin, а именно:gawk -F"|" "{print $13}" files*.csv
Элидио Маркина,

10
Я думаю, что это не работает, когда есть строки, содержащие запятую, то есть...,"string,string",...
нитрат натрия

Я думаю, что для 1-го и последнего столбца это будет некоторая ошибка. Первый столбец будет начинаться, "а последний заканчиваться"
BigTailWolf

Некоторые программы возвращают файлы CSV с разными разделителями, поэтому может потребоваться соответствующее изменение регулярного выражения. Пример разделителя точкой с запятой: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

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


8
Если столбец два не содержит запятую, в этом случае вы получите вторую половину столбца два. Речь идет о пункте <col1>, "3,000", <col2>. Однако мой ответ не намного лучше в отношении этой проблемы. Так что не расстраивайтесь.
синтезаторпатель

@synthesizerpatel Согласен, лучше использоватьawk
MattSizzle

1
Мы не уверены, что его CSV-файл содержит двойные кавычки, чтобы различать разные значения. Было бы лучше, если бы он предоставил входной файл, чтобы мы могли оценить наиболее подходящее решение.
Идрисс Нойманн,

51

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

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

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


14
Это должен быть принятый ответ. Этот инструмент знает, как работать с CSV-файлами, не ограничиваясь использованием запятой как разделителя полей. Чтобы извлечь 2-й столбец, «csvtool col 2 input.csv»
Владислав Довгалец

3
Просто предупреждаю ... если вы хотите использовать csvtool со стандартным вводом (пример csv поступает из другой команды), это что-то вроде этого. cat input.csv | csvtool formath '%(2)\n' -Примечание. Я знаю, что здесь cat бесполезен, но использовать его для любой команды, которая обычно экспортирует csv.
General Redneck

Если есть многострочные поля, format '%(2)\n'команда не может определить, где заканчивается одно поле. (csvtool 1.4.2)
Ярно

1
Более новые версии, csvtoolпохоже, требуют использования -в качестве входного имени файла для чтения из stdin.
Коннор Кларк

@GeneralRedneck, зачем использовать кошку? и это формат, а не форматcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

15

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

cat textfile.tsv | cut -f2 -s

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


тоже самое простое, и его легче адаптировать, чем в других примерах. Спасибо!
Ник Дженнингс,

6
Придирчиво, но в catэтом нет необходимости:< textfile.tsv cut -f2 -s
Анн ван Россум

8

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

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

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

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

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


1
Я пробовал это, и он по-прежнему считает запятые внутри цитируемых полей.
Дэниел С. Собрал

5

Другие ответы работают хорошо, но поскольку вы запросили решение, используя только оболочку 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 (внутреннего разделителя полей), который оболочка использует, чтобы знать, что разделяет поля (блоки текста). Так сказать IFS = - это все равно, что сказать, что "a, b" то же самое, что "a b" было бы, если бы IFS = "" (что и есть по умолчанию).

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

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

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


5

Вы можете использовать GNU Awk, см. Эту статью руководства пользователя . В качестве улучшения решения, представленного в статье (июнь 2015 г.), следующая команда gawk позволяет использовать двойные кавычки внутри полей с двойными кавычками; там двойная кавычка помечается двумя последовательными двойными кавычками (""). Кроме того, это позволяет пустые поля, но даже это не может обрабатывать многострочные поля . В следующем примере печатается 3-й столбец ( c=3переход) textfile.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 как разрыв строки, см. Википедию .

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

#!/bin/bash
gawk -- '
BEGIN{
    RS="\0" # 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($0, 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 может использоваться для обработки произвольных больших файлов.


5

Вот пример файла csv с 2 столбцами

myTooth.csv

Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom

Чтобы получить первый столбец, используйте:

cut -d, -f1 myTooth.csv

f обозначает поле, а d обозначает разделитель

Выполнение указанной выше команды приведет к следующему результату.

Вывод

Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28

Чтобы получить только 2-й столбец:

cut -d, -f2 myTooth.csv

А вот и результат Output

Tooth
wisdom
canine
canine
wisdom
incisor

Другой вариант использования:

Ваш входной файл csv содержит 10 столбцов, и вам нужны столбцы со 2 по 5 и столбцы 8, используя запятую в качестве разделителя ".

cut использует -f (что означает «поля») для указания столбцов и -d (что означает «разделитель»), чтобы указать разделитель. Вам необходимо указать последнее, потому что в некоторых файлах могут использоваться пробелы, табуляции или двоеточия для разделения столбцов.

cut -f 2-5,8 -d , myvalues.csv

cut - это командная утилита, и вот еще несколько примеров:

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]

4

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

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

4

Сначала мы создадим базовый CSV

[dumb@one pts]$ cat > file 
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

Тогда получаем 1-й столбец

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

3
csvtool col 2 file.csv 

где 2 - интересующий вас столбец

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

csvtool col 1,2 file.csv 

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


3

Я думаю, что проще всего использовать csvkit :

Получает 2-й столбец: csvcut -c 2 file.csv

Однако есть также csvtool и, вероятно, ряд других инструментов csv bash:

sudo apt-get install csvtool (для систем на базе Debian)

Это вернет столбец с первой строкой с идентификатором. csvtool namedcol ID csv_file.csv

Это вернет четвертую строку: csvtool col 4 csv_file.csv

Если вы хотите удалить строку заголовка:

csvtool col 4 csv_file.csv | sed '1d'


2

Интересно, почему ни в одном из ответов до сих пор не упоминается csvkit.

csvkit - это набор инструментов командной строки для преобразования и работы с CSV.

документация csvkit

Я использую его исключительно для управления данными csv, и пока я не нашел проблемы, которую я не мог бы решить с помощью cvskit.

Чтобы извлечь один или несколько столбцов из файла cvs, вы можете использовать csvcutслужебную программу, которая является частью набора инструментов. Чтобы извлечь второй столбец, используйте эту команду:

csvcut -c 2 filename_in.csv > filename_out.csv 

справочная страница csvcut

Если строки в csv цитируются, добавьте символ кавычки с qопцией:

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

Установите с помощью pip install csvkitили sudo apt install csvkit.



0

Некоторое время я использовал этот код, он не будет «быстрым», если вы не посчитаете «вырезание и вставку из 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 , "$1" "$2"; }

# Return Nth field in string of fields separated
# by SEP, fields numbered starting with 1
fldN() {
        local me="fldN: "
        local sep="$1"
        local fldnum="$2"
        local vals="$3"
        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

0

Вы также можете использовать цикл while

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv

Этот код выдает предупреждение Shellcheck : SC2034 . Поиск возвращает этот вопрос в качестве первого результата при поиске способов обойти предупреждение.
jww
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.