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.
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:
Anda bisa menggunakan awk untuk ini. Ubah '$ 2' ke kolom n yang Anda inginkan.
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
dan terakhir akan diakhiri dengan"
awk -F "\"*;\"*" '{print $2}' textfile.csv
Iya. cat mycsv.csv | cut -d ',' -f3
akan mencetak kolom ke-3.
awk
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.
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.
format '%(2)\n'
perintah tidak dapat memberi tahu di mana satu bidang berakhir. (csvtool 1.4.2)
csvtool
tampaknya perlu digunakan -
sebagai nama file masukan untuk membaca dari stdin.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
Mendarat di sini mencari untuk mengekstrak dari file yang dipisahkan tab. Pikir saya akan menambahkan.
cat textfile.tsv | cut -f2 -s
Di mana -f2
mengekstrak 2, kolom yang diindeks bukan nol, atau kolom kedua.
cat
tidak perlu:< textfile.tsv cut -f2 -s
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.
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 < file
ini 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.
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 dos2unix
untuk 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.
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 ...]
Saya membutuhkan penguraian CSV yang tepat, bukan cut
/ awk
dan 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
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
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'
Saya bertanya-tanya mengapa sejauh ini tidak ada jawaban yang menyebutkan csvkit.
csvkit adalah seperangkat alat baris perintah untuk mengubah dan bekerja dengan CSV
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 csvcut
utilitas yang merupakan bagian dari toolbox. Untuk mengekstrak kolom kedua gunakan perintah ini:
csvcut -c 2 filename_in.csv > filename_out.csv
Jika string di csv dikutip, tambahkan karakter kutipan dengan q
opsi:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Pasang dengan pip install csvkit
atau sudo apt install csvkit
.
Anda tidak dapat melakukannya tanpa pengurai CSV lengkap.
cut
dihitung?
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
Anda juga dapat menggunakan while loop
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
akan mencetak2
alih-alih2,3,4,5
.