Як витягти один стовпчик CSV-файлу


111

Якщо у мене є файл csv, чи існує швидкий спосіб башти, щоб надрукувати вміст будь-якого одного стовпця? Можна впевнено припустити, що кожен рядок має однакову кількість стовпців, але вміст кожного стовпця матиме різну довжину.

Відповіді:


136

Ви можете використати 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 в Windows, ви можете виконати той самий команд, що і @IgorMikushkin, таким чином:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
Я думаю, що це не вдається, коли є рядки, які містять кому, тобто...,"string,string",...
натрійнітрат

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

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

88

так. cat mycsv.csv | cut -d ',' -f3надрукує 3-й стовпчик.


8
Якщо стовпець другий містить кому, у цьому випадку ви отримаєте другу половину стовпця. Справа в точці <col1>, "3000", <col2>. Моя відповідь не набагато краща щодо цієї проблеми. Тож не розбивайтесь.
синтезаторпатель

@synthesizerpatel Я згоден краще використовуватиawk
MattSizzle

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

51

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

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

Заміна 2 на номер стовпця ефективно витягує дані стовпців, які ви шукаєте.


14
Це має бути прийнятою відповіддю. Цей інструмент знає, як поводитися з файлами CSV, далеко не розглядаючи кому як роздільник поля. Щоб витягнути другий стовпчик, "csvtool col 2 input.csv"
Владислав Довгалець

3
Просто голова вгору ... якщо ви хочете використовувати csvtool зі стандартним входом (наприклад, csv походить від іншої команди), це щось на кшталт цієї cat input.csv | csvtool formath '%(2)\n' -примітки. Я знаю, що кішка тут марна, але підпорядковуйте її будь-якій команді, яка звичайно експортує файл csv.
Генерал Реднєк

У ньому є багаторядкові поля, format '%(2)\n'команда не могла сказати, де закінчується одне поле. (csvtool 1.4.2)
jarno

1
Більше нові версії, csvtoolздається, вимагають використання -в якості вхідного імені файлу для читання з stdin.
Коннор Кларк

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

14

Приземлився тут, шукаючи витяг із файлу, розділеного вкладками Думав, я додам.

cat textfile.tsv | cut -f2 -s

Де -f2витягує 2, ненульовий індексований стовпець або другий стовпчик.


простий, теж точний і легше адаптується, ніж інші приклади. Дякую!
Нік Дженнінгс

6
Нітчікінг, але catзайвий:< textfile.tsv cut -f2 -s
Енн ван Россум

8

Багато відповідей на ці питання чудові, а деякі навіть заглядають у кутові справи. Я хотів би додати просту відповідь, яка може бути щоденною у використанні ... де ви здебільшого потрапляєте до тих кутових справ (як, наприклад, уникнувши коми чи коми в лапках тощо).

FS (Field Separator) - це змінна, значення якої задано пробілом. Тож awk за замовчуванням розпадається на простір для будь-якого рядка.

Таким чином, використовуючи BEGIN (Виконати перед тим, як взяти ввід), ми можемо встановити це поле на все, що завгодно ...

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

Вищевказаний код надрукує 3-й стовпчик у файлі csv.


1
Я спробував це, і він як і раніше вважає коми всередині цитованих полів.
Даніель К. Собрал

5

Інші відповіді працюють добре, але оскільки ви попросили рішення, використовуючи лише bash shell, ви можете зробити це:

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 (Internal Field Separator), що використовується оболонкою, щоб знати, що розділяє поля (блоки тексту). Отже, сказати IFS =, це як сказати "a, b" - це те саме, що "a b" було б, якби IFS = "" (що це за замовчуванням.)

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

  • do echo "${csv_line[0]}";done < file- зараз ми перебуваємо у фазі "робити", і ми говоримо відлуння 0-го елемента масиву "csv_line". Ця дія повторюється в кожному рядку файлу. < fileЧастина просто говорить той час циклу , де читати. ПРИМІТКА: пам'ятайте, що в bash масиви індексуються 0, тому перший стовпець є 0-м елементом.

Отож, у вас це є, витягуючи стовпчик з CSV в оболонці. Інші рішення, ймовірно, більш практичні, але це чистий баш.


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 як розрив рядків, див. Вікіпедія .

Якщо вхід може містити багаторядкові поля, ви можете використовувати наступний скрипт. Зверніть увагу на використання спеціального рядка для розділення записів у висновку (оскільки новий рядок роздільника за замовчуванням може виникнути в межах запису). Знову наступний приклад друкує 3-й стовпець (через 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

Щоб отримати лише другий стовпець:

cut -d, -f2 myTooth.csv

А ось вихідний вихід

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, але маки йдуть з рубіном, тож ви можете:

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 , і, мабуть, ряд інших інструментів bash для базування :

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

Це поверне стовпець, у першому рядку якого є "ID". 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.

Для витягування одного або декількох стовпців із файлу резюме можна скористатись 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.