潜在的に100k +行を含むCSVを解析しようとしています。ここに私が持っている基準があります:
指定されたインデックス(カンマで区切られた)で指定された値を持つCSV内のすべての行を取得したいと思います。
パフォーマンスについて特別に考慮したアイデアはありますか?
プレーンな古いgrep
とcut
を使用した最初のプロトタイプ:
grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}
これで十分に高速で適切な出力が得られれば、完了です。 :)
cut
-またはawk
ベースのワンライナーの代替として、特殊な csvtool
aka ocaml-csv
:
$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"
ドキュメントによると、エスケープ、引用などを処理します。
このYouTubeビデオを参照してください: BASHスクリプトレッスン10 CSVファイルの操作
CSVファイル:
Bob Brown;Manager;16581;Main
Sally Seaforth;Director;4678;HOME
Bashスクリプト:
#!/bin/bash
OLDIFS=$IFS
IFS=";"
while read user job uid location
do
echo -e "$user \
======================\n\
Role :\t $job\n\
ID :\t $uid\n\
SITE :\t $location\n"
done < $1
IFS=$OLDIFS
出力:
Bob Brown ======================
Role : Manager
ID : 16581
SITE : Main
Sally Seaforth ======================
Role : Director
ID : 4678
SITE : HOME
CSVはそれほど単純ではありません。持っているデータの制限に応じて、引用符で囲まれた値(コンマや改行を含む場合がある)や引用符のエスケープについて心配する必要があるかもしれません。
したがって、データが十分に制限されている場合は、単純なコンマ分割で問題なく処理できれば、シェルスクリプトで簡単に実行できます。一方、CSVを「適切に」解析する必要がある場合、bashは最初の選択肢ではありません。代わりに、高レベルのスクリプト言語、たとえばPython with csv.reader を見てください。
CSVファイルでは、各フィールドはコンマで区切られています。問題は、フィールド自体に埋め込みコンマが含まれている可能性があることです。
Name,Phone
"Woo, John",425-555-1212
フィールドセパレータとしてコンマを使用する代わりに、堅牢なCSVサポートを提供するライブラリパッケージが本当に必要です。 Pythonなどのスクリプト言語がこのようなサポートを持っていることを知っています。しかし、私はTclスクリプト言語に慣れているので、それが私が使用するものです。ために:
#!/usr/bin/env tclsh
package require csv
package require Tclx
# Parse the command line parameters
lassign $argv fileName columnNumber expectedValue
# Subtract 1 from columnNumber because Tcl's list index starts with a
# zero instead of a one
incr columnNumber -1
for_file line $fileName {
set columns [csv::split $line]
set columnValue [lindex $columns $columnNumber]
if {$columnValue == $expectedValue} {
puts $line
}
}
このスクリプトをcsv.tclというファイルに保存し、次のように呼び出します。
$ tclsh csv.tcl filename indexNumber expectedValue
このスクリプトはCSVファイルを1行ずつ読み取り、その行を変数$ lineに格納してから、各行を列のリスト(変数$ columns)に分割します。次に、指定された列を取り出し、$ columnValue変数に割り当てます。一致する場合は、元の行を印刷します。
awk
を使用:
export INDEX=2
export VALUE=bar
awk -F, '$'$INDEX' ~ /^'$VALUE'$/ {print}' inputfile.csv
編集:Dennis Williamson's 優れたコメントによると、これはawk変数を使用して定義することにより、よりきれいに(そして安全に)書けます-v
スイッチ:
awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' inputfile.csv
Jeez ...変数とすべてのもので、awkはほとんど 実際のプログラミング言語 ...です.
データに特殊文字が含まれていない状況では、Nate Kohlとghostdog74が提案するソリューションが適しています。
データのフィールド内にコンマまたは改行が含まれている場合、awkはフィールド番号を適切にカウントしない可能性があり、誤った結果が得られます。
私が書いたcsvquote( https://github.com/dbro/csvquote で利用可能)からの助けを借りて、awkを引き続き使用できます。
csvquote inputfile.csv | awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' | csvquote -u
このプログラムは、引用符で囲まれたフィールド内の特殊文字を検索し、awkを混乱させない非印刷文字に一時的に置き換えます。その後、awkの実行後に復元されます。
index=1
value=2
awk -F"," -v i=$index -v v=$value '$(i)==v' file
sed
またはawk
のソリューションはおそらく短いでしょうが、Perlの場合は次のとおりです。
Perl -F/,/ -ane 'print if $F[<INDEX>] eq "<VALUE>"`
ここで、<INDEX>
は0から始まります(最初の列に0、2番目の列に1など)。
引用符をサポートし、VMware vMAアプライアンスに派手なものをインストールする必要のないエレガントなソリューションを探していました。このシンプルなpythonスクリプトがうまくいくことがわかりました!(スクリプトにcsv2tsv.py
(CSVをタブ区切り値に変換するため-TSV)
#!/usr/bin/env python
import sys, csv
with sys.stdin as f:
reader = csv.reader(f)
for row in reader:
for col in row:
print col+'\t',
print
タブで区切られた値は、cutコマンドを使用して簡単に分割できます(区切り文字を指定する必要はありません。タブがデフォルトです)。サンプルの使用法/出力は次のとおりです。
> esxcli -h $VI_Host --formatter=csv network vswitch standard list |csv2tsv.py|cut -f12
Uplinks
vmnic4,vmnic0,
vmnic5,vmnic1,
vmnic6,vmnic2,
私のスクリプトでは、実際にtsv出力を1行ずつ解析し、readまたはcutを使用して必要なフィールドを取得します。