Cara mengekstrak satu kolom file csv


111

Jika saya memiliki file csv, apakah ada cara bash cepat untuk mencetak konten dari satu kolom saja? Dapat diasumsikan bahwa setiap baris memiliki jumlah kolom yang sama, tetapi setiap konten kolom akan memiliki panjang yang berbeda.

Jawaban:


136

Anda bisa menggunakan awk untuk ini. Ubah '$ 2' ke kolom n yang Anda inginkan.

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

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'akan mencetak 2alih-alih 2,3,4,5.
Igor Mikushkin

Jika Anda adalah orang yang beruntung menggunakan GNU Tools di Windows, Anda dapat menjalankan perintah yang sama dengan @IgorMikushkin sebagai berikut:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
Saya pikir ini gagal ketika ada string yang mengandung koma, yaitu...,"string,string",...
sodiumnitrate

Saya pikir untuk colume pertama dan terakhir, ini akan memiliki beberapa kekurangan. Kolom pertama akan dimulai dengan "dan terakhir akan diakhiri dengan"
BigTailWolf

Beberapa program mengembalikan file CSV dengan pembatas berbeda, jadi mungkin diperlukan untuk mengubah ekspresi reguler yang sesuai. Contoh untuk pemisah titik koma: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

Iya. cat mycsv.csv | cut -d ',' -f3akan mencetak kolom ke-3.


8
Kecuali kolom dua berisi koma dalam hal ini Anda akan mendapatkan paruh kedua kolom dua. Contoh kasus di poin <col1>, "3,000", <col2>. Jawaban saya tidak jauh lebih baik sehubungan dengan masalah itu. Jadi jangan kecewa.
synthesizerpatel

@synthesizerpatel Saya setuju lebih baik untuk menggunakanawk
MattSizzle

1
Kami tidak yakin bahwa file CSV miliknya berisi tanda kutip ganda untuk membedakan nilai-nilai yang berbeda. Akan lebih baik jika dia memberikan file masukan sehingga kami dapat menilai solusi yang paling tepat.
Idriss Neumann

50

Cara paling sederhana saya bisa menyelesaikan ini adalah dengan hanya menggunakan csvtool . Saya memiliki kasus penggunaan lain juga untuk menggunakan csvtool dan dapat menangani tanda kutip atau pembatas dengan tepat jika muncul dalam data kolom itu sendiri.

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

Mengganti 2 dengan nomor kolom akan secara efektif mengekstrak data kolom yang Anda cari.


14
Ini harus menjadi jawaban yang diterima. Alat ini tahu cara menangani file CSV, lebih dari sekadar memperlakukan koma sebagai pemisah bidang. Untuk mengekstrak kolom ke-2, "csvtool col 2 input.csv"
Vladislavs Dovgalecs

3
Tunggu dulu ... jika Anda ingin menggunakan csvtool dengan input standar (contoh csv berasal dari perintah lain) itu seperti ini cat input.csv | csvtool formath '%(2)\n' -Catatan Saya tahu cat di sini tidak berguna tetapi sub untuk perintah apa pun yang biasanya mengekspor csv.
Jenderal Redneck

Jika ada bidang multiline, format '%(2)\n'perintah tidak dapat memberi tahu di mana satu bidang berakhir. (csvtool 1.4.2)
jarno

1
Versi yang lebih baru csvtooltampaknya perlu digunakan -sebagai nama file masukan untuk membaca dari stdin.
Connor Clark

@GeneralRedneck kenapa menggunakan cat? dan formatnya bukan formatcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

14

Mendarat di sini mencari untuk mengekstrak dari file yang dipisahkan tab. Pikir saya akan menambahkan.

cat textfile.tsv | cut -f2 -s

Di mana -f2mengekstrak 2, kolom yang diindeks bukan nol, atau kolom kedua.


sederhana, juga intinya, dan lebih mudah beradaptasi dibandingkan contoh lainnya. Terima kasih!
Nick Jennings

6
Nitpicking, tetapi cattidak perlu:< textfile.tsv cut -f2 -s
Anne van Rossum

8

Banyak jawaban untuk pertanyaan-pertanyaan ini bagus dan beberapa bahkan telah menyelidiki kasus-kasus sudut. Saya ingin menambahkan jawaban sederhana yang dapat digunakan sehari-hari ... di mana Anda kebanyakan masuk ke kasus sudut tersebut (seperti tidak menggunakan koma atau koma dalam tanda kutip dll,).

FS (Field Separator) adalah variabel yang nilainya disimpangkan ke spasi. Jadi awk secara default membagi ruang untuk baris mana pun.

Jadi dengan menggunakan BEGIN (Execute before taking input) kita dapat mengatur field ini menjadi apapun yang kita inginkan ...

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

Kode di atas akan mencetak kolom ke-3 di file csv.


1
Saya sudah mencoba ini, dan masih menganggap koma di dalam bidang yang dikutip.
Daniel C. Sobral

5

Jawaban lain berfungsi dengan baik, tetapi karena Anda meminta solusi hanya dengan menggunakan bash shell, Anda dapat melakukan ini:

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

Dan kemudian Anda dapat menarik kolom (yang pertama dalam contoh ini) seperti ini:

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

Jadi ada beberapa hal yang terjadi di sini:

  • while IFS=,- Ini berarti menggunakan koma sebagai IFS (Internal Field Separator), yang digunakan shell untuk mengetahui apa yang memisahkan bidang (blok teks). Jadi mengatakan IFS =, seperti mengatakan "a, b" sama dengan "a b" akan menjadi jika IFS = "" (yang secara default.)

  • read -a csv_line; - ini mengatakan membaca di setiap baris, satu per satu dan membuat larik di mana setiap elemen disebut "csv_line" dan mengirimkannya ke bagian "lakukan" di loop sementara kami

  • do echo "${csv_line[0]}";done < file- sekarang kita berada dalam fase "lakukan", dan kita mengatakan echo elemen ke 0 dari array "csv_line". Tindakan ini diulangi di setiap baris file. Bagian < fileini hanya memberi tahu loop while dari mana harus membaca. CATATAN: ingat, dalam bash, array diindeks 0, jadi kolom pertama adalah elemen ke-0.

Jadi begitulah, menarik kolom dari CSV di shell. Solusi lain mungkin lebih praktis, tetapi yang ini murni pesta.


5

Anda dapat menggunakan GNU Awk, lihat artikel panduan pengguna ini . Sebagai peningkatan solusi yang disajikan dalam artikel (pada bulan Juni 2015), perintah gawk berikut memungkinkan tanda kutip ganda di dalam bidang tanda kutip ganda; kutipan ganda ditandai dengan dua tanda kutip ganda berturut-turut ("") di sana. Selain itu, ini memungkinkan bidang kosong, tetapi ini pun tidak dapat menangani bidang multiline . Contoh berikut mencetak kolom ke-3 (melalui c=3) dari 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)

Catat penggunaan dari dos2unixuntuk mengkonversi kemungkinan jeda baris gaya DOS (CRLF yaitu "\ r \ n") dan pengkodean UTF-16 (dengan tanda urutan byte), masing-masing menjadi "\ n" dan UTF-8 (tanpa tanda urutan byte). File CSV standar menggunakan CRLF sebagai pemisah baris, lihat Wikipedia .

Jika masukan mungkin berisi bidang multiline, Anda dapat menggunakan skrip berikut. Perhatikan penggunaan string khusus untuk memisahkan rekaman dalam output (karena baris baru pemisah default dapat terjadi dalam rekaman). Sekali lagi, contoh berikut mencetak kolom ke-3 (melalui c=3) dari 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)

Ada pendekatan lain untuk masalah tersebut. csvquote dapat menampilkan konten file CSV yang dimodifikasi sehingga karakter khusus di dalam bidang diubah sehingga alat pengolah teks Unix biasa dapat digunakan untuk memilih kolom tertentu. Misalnya kode berikut mengeluarkan kolom ketiga:

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

csvquote dapat digunakan untuk memproses file besar yang sewenang-wenang.


5

Berikut adalah contoh file csv dengan 2 kolom

myTooth.csv

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

Untuk mendapatkan kolom pertama, gunakan:

cut -d, -f1 myTooth.csv

f adalah singkatan dari Field dan d adalah singkatan dari delimiter

Menjalankan perintah di atas akan menghasilkan keluaran sebagai berikut.

Keluaran

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

Untuk mendapatkan kolom ke-2 saja:

cut -d, -f2 myTooth.csv

Dan di sini adalah output output

Tooth
wisdom
canine
canine
wisdom
incisor

Kasus penggunaan lain:

File input csv Anda berisi 10 kolom dan Anda menginginkan kolom 2 hingga 5 dan kolom 8, menggunakan koma sebagai pemisah ".

cut menggunakan -f (artinya "kolom") untuk menentukan kolom dan -d (artinya "pembatas") untuk menentukan pemisah. Anda perlu menentukan yang terakhir karena beberapa file mungkin menggunakan spasi, tab, atau titik dua untuk memisahkan kolom.

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

cut adalah utilitas perintah dan berikut beberapa contoh lainnya:

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

4

Saya membutuhkan penguraian CSV yang tepat, bukan cut/ awkdan doa. Saya mencoba ini di mac tanpa csvtool, tetapi mac memang dilengkapi dengan ruby, jadi Anda dapat melakukan:

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

4

Pertama kita akan membuat CSV dasar

[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

Kemudian kita mendapatkan kolom pertama

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

3
csvtool col 2 file.csv 

dengan 2 adalah kolom yang Anda minati

Anda juga bisa melakukannya

csvtool col 1,2 file.csv 

untuk melakukan banyak kolom


3

Saya pikir yang paling mudah adalah menggunakan csvkit :

Mendapatkan kolom ke-2: csvcut -c 2 file.csv

Namun, ada juga csvtool , dan mungkin sejumlah alat bash csv lain di luar sana:

sudo apt-get install csvtool (untuk sistem berbasis Debian)

Ini akan mengembalikan kolom dengan baris pertama memiliki 'ID' di dalamnya. csvtool namedcol ID csv_file.csv

Ini akan mengembalikan baris keempat: csvtool col 4 csv_file.csv

Jika Anda ingin melepaskan baris header:

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


2

Saya bertanya-tanya mengapa sejauh ini tidak ada jawaban yang menyebutkan csvkit.

csvkit adalah seperangkat alat baris perintah untuk mengubah dan bekerja dengan CSV

dokumentasi csvkit

Saya menggunakannya secara eksklusif untuk manajemen data csv dan sejauh ini saya belum menemukan masalah yang tidak dapat saya selesaikan menggunakan cvskit.

Untuk mengekstrak satu atau lebih kolom dari file cvs Anda dapat menggunakan csvcututilitas yang merupakan bagian dari toolbox. Untuk mengekstrak kolom kedua gunakan perintah ini:

csvcut -c 2 filename_in.csv > filename_out.csv 

halaman referensi csvcut

Jika string di csv dikutip, tambahkan karakter kutipan dengan qopsi:

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

Pasang dengan pip install csvkitatau sudo apt install csvkit.


1

Anda tidak dapat melakukannya tanpa pengurai CSV lengkap.


1
Kapan sesuatu dianggap sebagai parser CSV lengkap? Apakah cutdihitung?
HelloGoodbye

0

Telah menggunakan kode ini untuk sementara waktu, ini tidak "cepat" kecuali Anda menghitung "memotong dan menempel dari stackoverflow".

Ini menggunakan operator $ {##} dan $ {%%} dalam satu loop, bukan IFS. Ini memanggil 'err' dan 'die', dan hanya mendukung koma, tanda hubung, dan pipa sebagai karakter SEP (hanya itu yang saya butuhkan).

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*}
}

Contoh:

$ 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

Anda juga dapat menggunakan while loop

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

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

Kode ini menghasilkan peringatan Shellcheck : SC2034 . Penelusuran mengembalikan pertanyaan ini sebagai hasil pertama saat mencari cara untuk menghindari peringatan.
jww
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.