Bagaimana cara mengurai file CSV di Bash?

112

Saya sedang mengerjakan skrip Bash yang panjang. Saya ingin membaca sel dari file CSV menjadi variabel Bash. Saya dapat mengurai baris dan kolom pertama, tetapi tidak dapat mengurai kolom lain. Inilah kode saya sejauh ini:


  cat myfile.csv|while read line
  do
    read -d, col1 col2 < <(echo $line)
    echo "I got:$col1|$col2"
  done

Ini hanya mencetak kolom pertama. Sebagai tes tambahan, saya mencoba yang berikut ini:

read -d, x y < <(echo a,b,)

Dan $ y kosong. Jadi saya mencoba:

read x y < <(echo a b)

Dan $ y adalah b. Mengapa?

Pengguna1
sumber
7
Anda dianggap awkmenggunakan $1, $2, dll?
BeemerGuy
4
sebagai sidenote: command <<(echo "string") ---> command <<< "string"
tokland
1
Program baris perintah 'potong' dirancang untuk itu: ss64.com/bash/cut.html
Jay

Jawaban:

215

Anda perlu menggunakan IFSalih-alih -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 fitur khusus yang dapat menangani kolom kutipan dengan koma internal, di antara masalah lain yang tidak dapat ditangani Bash sendiri. Contoh alat tersebut adalah cvstooldan csvkit.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
7
Solusi yang diusulkan baik-baik saja untuk file CSV yang sangat sederhana, yaitu, jika header dan nilai bebas dari koma dan tanda kutip yang disematkan. Sebenarnya cukup sulit untuk menulis parser CSV generik (terutama karena ada beberapa "standar" CSV). Salah satu pendekatan untuk membuat file CSV lebih sesuai dengan alat * nix adalah dengan mengonversinya menjadi TSV (nilai yang dipisahkan tab), misalnya menggunakan Excel.
puncak
Menariknya, saya tidak bisa melakukan mkdir di body. Saya mendapatkan command not found. Hanya echopekerjaannya.
Zsolt
1
@Zsolt: Tidak ada alasan untuk itu. Anda harus memiliki kesalahan ketik atau karakter non-cetak yang tersesat.
Dijeda sampai pemberitahuan lebih lanjut.
2
@DennisWilliamson Anda harus menyertakan pemisah misalnya saat menggunakan ;:while IFS=";" read col1 col2; do ...
thomas.mc.work
1
@ thomas.mc.work: Itu benar dalam kasus titik koma dan karakter lain yang khusus untuk shell. Dalam kasus koma, itu tidak perlu dan saya cenderung memilih untuk menghilangkan karakter yang tidak diperlukan. Misalnya, Anda selalu dapat menentukan variabel untuk ekspansi menggunakan tanda kurung kurawal (misalnya ${var}), tetapi saya menghilangkannya jika tidak diperlukan. Bagi saya, ini terlihat lebih bersih.
Dijeda sampai pemberitahuan lebih lanjut.
10

Dari manhalaman:

-d delim Karakter pembatas pertama digunakan untuk mengakhiri baris input, bukan baris baru.

Anda menggunakan -d,yang akan menghentikan baris input pada koma. Itu tidak akan membaca sisa baris. Itulah mengapa $ y kosong.

dogbane
sumber
3

Kita dapat mengurai 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 mengurai bidang string ke variabel dan tr menghapus kutipan.

Sedikit lebih lambat karena awk dijalankan untuk setiap bidang.

Maithilish
sumber
1
Bagus, Anda juga bisa menggunakan koma (,)
pkarc
0

Jika Anda ingin membaca file CSV dengan beberapa baris, maka ini solusinya.

while IFS=, read -ra line
do 
    test $i -eq 1 && ((i=i+1)) && continue
    for col_val in ${line[@]}
    do
        echo -n "$col_val|"                 
    done
    echo        
done < "$csvFile"
Eliya
sumber