Jika saya memiliki file csv, ada bash cepat cara untuk mencetak isi dari hanya salah satu kolom? Ini adalah aman untuk mengasumsikan bahwa setiap baris memiliki jumlah kolom yang sama, tetapi masing-masing kolom's konten akan memiliki panjang yang berbeda.
Cara paling mudah saya bisa mendapatkan ini dilakukan adalah untuk hanya menggunakan csvtool. Saya punya kasus penggunaan lainnya juga untuk menggunakan csvtool dan dapat menangani kutipan atau pembatas tepat jika mereka muncul dalam kolom data itu sendiri.
csvtool format '%(2)\n' input.csv
Mengganti 2 dengan jumlah kolom akan secara efektif ekstrak data kolom yang anda cari.
Banyak jawaban untuk pertanyaan ini adalah besar dan beberapa bahkan melihat ke sudut kasus. Saya ingin menambahkan jawaban sederhana yang dapat digunakan sehari-hari... di mana sebagian besar anda bisa menjadi orang-orang corner kasus (seperti setelah lolos koma atau koma dalam kutipan dll.,).
FS (Field Separator) adalah variabel yang nilainya dafaulted untuk ruang. Jadi awk secara default perpecahan di ruang untuk setiap baris.
Jadi menggunakan BEGIN (Mengeksekusi sebelum mengambil input) kita dapat mengatur bidang ini untuk apa pun yang kita inginkan...
awk 'BEGIN {FS = ","}; {print $3}'
Kode di atas akan mencetak 3 kolom di file csv.
Dengan jawaban yang lain bekerja dengan baik, tetapi karena anda diminta untuk solusi hanya 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 keluar kolom (pertama dalam contoh ini) seperti:
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's beberapa hal yang terjadi di sini:
sementara IFS=,
- ini mengatakan untuk menggunakan koma sebagai IFS (Internal Field Separator), yang adalah apa yang shell menggunakan untuk tahu apa yang memisahkan bidang (blok teks). Jadi mengatakan IFS=, seperti mengatakan "b" adalah sama dengan "b" akan jika IFS=" " (yang ini apa yang itu adalah secara default.)
baca-csv_line;
- ini mengatakan baca di setiap baris, satu pada satu waktu dan membuat array di mana masing-masing elemen ini disebut "csv_line" dan mengirim bahwa untuk "tidak" bagian dari while loop
echo "${csv_line[0]";selesai < file
- sekarang kita're di "tidak" fase, dan kami're mengatakan echo elemen 0 array "csv_line". Tindakan ini diulang pada setiap baris dari file. The < file
bagian ini hanya menceritakan sementara lingkaran di mana untuk membaca dari. CATATAN: ingat, di bash, array adalah 0 diindeks, sehingga kolom pertama adalah elemen 0.
Jadi di sana anda memilikinya, menarik keluar sebuah kolom dari sebuah CSV in the shell. Solusi lain yang mungkin lebih praktis, tapi yang satu ini adalah murni bash.
Anda bisa menggunakan GNU Awk, lihat artikel ini dari user guide.
Sebagai perbaikan untuk solusi yang disajikan dalam artikel (juni 2015), berikut melongo perintah yang memungkinkan tanda kutip ganda dalam dua dikutip sawah; double quote ditandai dengan dua tanda kutip ganda ("") ada. Selain itu, hal ini memungkinkan bidang-bidang kosong, tapi ini bahkan tidak bisa menangani multiline bidang. Berikut contoh cetakan ke-3 kolom (via 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)
Perhatikan penggunaan dos2unix
untuk mengkonversi mungkin DOS style line breaks (CRLF yaitu "\r\n") dan encoding UTF-16 (dengan byte order mark) untuk "\n" dan UTF-8 (tanpa byte order mark), masing-masing. Standar file CSV menggunakan CRLF sebagai garis istirahat, lihat Wikipedia.
Jika input mungkin berisi multiline bidang, anda dapat menggunakan script berikut. Perhatikan penggunaan string khusus untuk memisahkan catatan dalam output (karena default pemisah newline bisa terjadi dalam sebuah catatan). Lagi-lagi, berikut ini contoh cetakan ke-3 kolom (via 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 ini. csvquote dapat menampilkan isi dari file CSV dimodifikasi sehingga karakter khusus dalam bidang yang berubah sehingga biasa Unix teks pengolahan alat-alat yang dapat digunakan untuk memilih kolom tertentu. Untuk contoh kode berikut output kolom ketiga:
csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u
csvquote
dapat digunakan untuk proses sewenang-wenang file besar.
Aku butuh yang tepat CSV parsing, tidak cut
/ awk
dan doa. I'm mencoba ini di mac tanpa csvtool
, tapi mac tidak datang dengan ruby, sehingga anda dapat melakukan:
echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
Saya pikir yang paling mudah adalah dengan menggunakan csvkit:
Mendapat 2 kolom:
csvcut -c 2 file.csv
Namun, ada's juga csvtool, dan mungkin sejumlah lainnya csv bash alat-alat di luar sana:
sudo apt-get install csvtool
(untuk Debian berbasis sistem)
Ini akan kembali 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 drop baris header:
csvtool col 4 csv_file.csv | sed '1d'
Aku bertanya-tanya mengapa tidak ada jawaban yang sejauh ini telah disebutkan csvkit.
csvkit adalah sebuah suite alat-alat baris perintah untuk mengkonversi ke dan bekerja dengan CSV
Saya menggunakannya secara eksklusif untuk data csv manajemen dan sejauh ini saya belum menemukan masalah yang saya tidak bisa memecahkan menggunakan cvskit.
Untuk ekstrak satu atau lebih kolom dari cvs file, anda dapat menggunakan csvcut
utilitas yang merupakan bagian dari toolbox. Untuk mengekstrak kolom kedua menggunakan perintah ini:
csvcut -c 2 filename_in.csv > filename_out.csv
Jika string di csv yang dikutip, tambahkan kutipan karakter dengan q
pilihan:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Install dengan pip menginstal csvkit
atau sudo apt-get install csvkit
.
Berikut ini adalah file csv contoh 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 singkatan dari Lapangan dan d adalah singkatan dari pembatas
Menjalankan perintah di atas akan menghasilkan output sebagai berikut.
Output
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
Use case lain:
Anda csv file input yang berisi 10 kolom dan anda ingin kolom 2 sampai 5 dan kolom 8, menggunakan koma sebagai pemisah".
potong menggunakan -f (arti "bidang") untuk menentukan kolom dan -d (yang berarti "pembatas") untuk menentukan pemisah. Anda perlu menentukan terakhir karena beberapa file mungkin menggunakan spasi, tab, atau titik dua untuk kolom yang terpisah.
cut -f 2-5,8 -d , myvalues.csv
cut adalah perintah utilitas dan berikut adalah beberapa contoh:
SYNOPSIS
cut -b list [-n] [file ...]
cut -c list [file ...]
cut -f list [-d delim] [-s] [file ...]
Menggunakan kode ini untuk sementara, hal ini tidak "cepat" kecuali jika anda menghitung "memotong dan paste dari stackoverflow".
Menggunakan ${##} dan ${%%} operator di lingkaran bukan IFS. Itu panggilan 'err' dan 'mati', dan hanya mendukung koma, dash, dan pipa sebagai SEP karakter (yang's semua 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