GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara mengurai file CSV di Bash?

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 .


Linux
  1. Bagaimana Anda menormalkan jalur file di Bash?

  2. Bagaimana cara menyorot skrip Bash di Vim?

  3. Cara grep bagian file di bash shell

  1. Bagaimana cara memeriksa syslog di Bash di Linux?

  2. Bagaimana cara memeriksa apakah suatu file kosong di Bash?

  3. Cara memasukkan file dalam skrip bash shell

  1. Bagaimana cara mendapatkan direktori absolut dari sebuah file di bash?

  2. Cara grep \n dalam file

  3. Bagaimana cara mengurai header HTTP menggunakan Bash?