Как извлечь один столбец из файла 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.