Bash で CSV ファイルを解析する方法
この質問に遅れて、bashが新しい機能を提供するように、この質問はbashに関するものであり、すでに投稿された回答のどれも正確にこれを行うこの強力で準拠した方法を示していないためです .
bash
の下の CSV ファイルを解析しています 、ロード可能モジュールを使用
RFC 4180 に準拠 、このサンプル CSV 行 のような文字列 :
12,22.45,"Hello, ""man"".","A, b.",42
として分割する必要があります
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
bash 読み込み可能 .C コンパイル済みモジュール。
bash では、ロード可能な C コンパイル モジュールを作成、編集、使用できます。 .読み込まれると、他のビルトインと同じように機能します !! (ソース ツリーで詳細を確認できます。;)
現在のソース ツリー (2021 年 10 月 15 日、bash V5.1-rc3) には多数のサンプルが含まれています:
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.
完全に機能する cvs
があります examples/loadables
ですぐに使えるパーサー ディレクトリ:csv.c !!
Debian GNU/Linux ベースのシステムでは、
によって bash-builtins パッケージをインストールする必要がある場合があります。apt install bash-builtins
ロード可能な bash-builtins の使用 :
次に:
enable -f /usr/lib/bash/csv csv
そこから、 csv
を使用できます bash 組み込みとして .
私のサンプルでは: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
次に、ループでファイルを処理します。
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
この方法は明らかに、bash ビルトインの他の組み合わせや任意のバイナリへの fork を使用するよりも、最も速くて強力です。
残念ながら、システムの実装によっては、bash のバージョンが loadable
なしでコンパイルされている場合 、これはうまくいかないかもしれません...
複数行の CSV フィールドを含む完全なサンプル
1 を含む小さなサンプル ファイルを次に示します。 見出し、4 列と 3 行。 2 つのフィールドに 改行 が含まれているため 、ファイルは 6 です 行の長さ。
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
そして、このファイルを正しく解析できる小さなスクリプト:
#!/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
これはレンダリングされる可能性があります:(私は printf "%q"
を使用しました 改行などの印刷できない文字を表す $'\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"
そこには完全な作業サンプルがあります:csvsample.sh.txt orcsvsample.sh.
警告:
もちろん、これを使用した CSV の解析は完璧ではありません!これは多くの単純な CSV ファイルで機能しますが、エンコーディングとセキュリティに注意してください!!サンプルとして、このモジュールはバイナリ フィールドを処理できません!
csv.c ソースコードのコメントと RFC 4180 をよく読んでください!
引用符で囲まれた文字列を含み、say | で区切られた csv ファイルを解析できます。次のコードで
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
文字列フィールドを変数と tr
に解析します 引用を削除します。
awk
のように少し遅い フィールドごとに実行されます。
man
から ページ:
-d delimdelim の最初の文字は、改行ではなく、入力行を終了するために使用されます。
-d,
を使用しています コンマで入力行を終了します。行の残りは読み取れません。そのため、$y は空です。
IFS
を使用する必要があります -d
の代わりに :
while IFS=, read -r col1 col2
do
echo "I got:$col1|$col2"
done < myfile.csv
汎用の CSV 解析では、Bash だけでは処理できない問題の中でも特に、引用符で囲まれたフィールドにカンマを使用して処理できる特殊なツールを使用する必要があることに注意してください。そのようなツールの例は cvstool
です と csvkit
.