Bagaimana cara mengurai file CSV di Bash?
Datang terlambat ke pertanyaan ini dan karena bash memang menawarkan fitur baru, karena pertanyaan ini berdiri tentang bash dan karena tidak ada jawaban yang sudah diposting menunjukkan cara yang kuat dan sesuai untuk melakukan tepatnya ini .
Mem-parsing file CSV di bawah bash
, menggunakan modul yang dapat dimuat
Sesuai dengan RFC 4180 , string seperti contoh baris CSV ini :
12,22.45,"Hello, ""man"".","A, b.",42
harus dipisah sebagai
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
bash dapat dimuat .C modul yang dikompilasi.
Di bawah bash, Anda dapat membuat, mengedit, dan menggunakan modul terkompilasi c yang dapat dimuat . Setelah dimuat, mereka berfungsi seperti bawaan lainnya !! (Anda dapat menemukan informasi lebih lanjut di pohon sumber.;)
Pohon sumber saat ini (15 Okt 2021, bash V5.1-rc3) memang mengandung banyak sampel:
accept listen for and accept a remote network connection on a given port
asort Sort arrays in-place
basename Return non-directory portion of pathname.
cat cat(1) replacement with no options - the way cat was intended.
csv process one line of csv data and populate an indexed array.
dirname Return directory portion of pathname.
fdflags Change the flag associated with one of bash's open file descriptors.
finfo Print file info.
head Copy first part of files.
hello Obligatory "Hello World" / sample loadable.
...
tee Duplicate standard input.
template Example template for loadable builtin.
truefalse True and false builtins.
tty Return terminal name.
uname Print system information.
unlink Remove a directory entry.
whoami Print out username of current user.
Ada cvs
yang berfungsi penuh parser siap digunakan di examples/loadables
direktori:csv.c !!
Di bawah sistem berbasis Debian GNU/Linux, Anda mungkin harus menginstal paket bash-builtins dengan
apt install bash-builtins
Menggunakan bash-builtin yang dapat dimuat :
Lalu:
enable -f /usr/lib/bash/csv csv
Dari sana, Anda dapat menggunakan csv
sebagai bash bawaan .
Dengan sampel saya:12,22.45,"Hello, ""man"".","A, b.",42
csv -a myArray '12,22.45,"Hello, ""man"".","A, b.",42'
printf "%s\n" "${myArray[@]}" | cat -n
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
Kemudian dalam satu lingkaran, memproses file.
while IFS= read -r line;do
csv -a aVar "$line"
printf "First two columns are: [ '%s' - '%s' ]\n" "${aVar[0]}" "${aVar[1]}"
done <myfile.csv
Cara ini jelas yang tercepat dan terkuat daripada menggunakan kombinasi lain dari bash builtin atau fork ke biner apa pun.
Sayangnya, bergantung pada implementasi sistem Anda, jika versi bash Anda dikompilasi tanpa loadable
, ini mungkin tidak berfungsi...
Sampel lengkap dengan bidang CSV multibaris.
Ini adalah file contoh kecil dengan 1 judul, 4 kolom dan 3 baris. Karena dua bidang berisi baris baru , filenya adalah 6 panjang garis.
Id,Name,Desc,Value
1234,Cpt1023,"Energy counter",34213
2343,Sns2123,"Temperatur sensor
to trigg for alarm",48.4
42,Eye1412,"Solar sensor ""Day /
Night""",12199.21
Dan skrip kecil dapat mengurai file ini dengan benar:
#!/bin/bash
enable -f /usr/lib/bash/csv csv
file="sample.csv"
exec {FD}<"$file"
read -ru $FD line
csv -a headline "$line"
printf -v fieldfmt '%-8s: "%%q"\\n' "${headline[@]}"
while read -ru $FD line;do
while csv -a row "$line" ; ((${#row[@]}<${#headline[@]})) ;do
read -ru $FD sline || break
line+=$'\n'"$sline"
done
printf "$fieldfmt\\n" "${row[@]}"
done
Ini mungkin merender:(Saya telah menggunakan printf "%q"
untuk mewakili karakter yang tidak dapat dicetak seperti baris baru sebagai $'\n'
)
Id : "1234"
Name : "Cpt1023"
Desc : "Energy\ counter"
Value : "34213"
Id : "2343"
Name : "Sns2123"
Desc : "$'Temperatur sensor\nto trigg for alarm'"
Value : "48.4"
Id : "42"
Name : "Eye1412"
Desc : "$'Solar sensor "Day /\nNight"'"
Value : "12199.21"
Anda dapat menemukan contoh lengkap yang berfungsi di sana:csvsample.sh.txt orcsvsample.sh.
Peringatan:
Tentu saja, penguraian CSV menggunakan ini tidaklah sempurna! Ini berfungsi untuk banyak file CSV sederhana, tetapi peduli dengan penyandian dan keamanan!! Sebagai contoh, modul ini tidak dapat menangani bidang biner!
Baca dengan cermat komentar kode sumber csv.c dan RFC 4180!
Kita dapat mem-parsing file csv dengan string yang dikutip dan dibatasi oleh say | dengan kode berikut
while read -r line
do
field1=$(echo "$line" | awk -F'|' '{printf "%s", $1}' | tr -d '"')
field2=$(echo "$line" | awk -F'|' '{printf "%s", $2}' | tr -d '"')
echo "$field1 $field2"
done < "$csvFile"
awk
mem-parsing bidang string ke variabel dan tr
menghapus kutipan.
Sedikit lebih lambat seperti awk
dieksekusi untuk setiap bidang.
Dari man
halaman:
-d delimKarakter pertama delim digunakan untuk mengakhiri baris masukan, bukan baris baru.
Anda menggunakan -d,
yang akan mengakhiri baris input pada koma. Itu tidak akan membaca sisa baris. Itu sebabnya $y kosong.
Anda perlu menggunakan IFS
bukannya -d
:
while IFS=, read -r col1 col2
do
echo "I got:$col1|$col2"
done < myfile.csv
Perhatikan bahwa untuk penguraian CSV tujuan umum, Anda harus menggunakan alat khusus yang dapat menangani bidang yang dikutip dengan koma internal, di antara masalah lain yang tidak dapat ditangani sendiri oleh Bash. Contoh alat tersebut adalah cvstool
dan csvkit
.