web-dev-qa-db-ja.com

Bashシェルスクリプト-CSV解析

潜在的に100k +行を含むCSVを解析しようとしています。ここに私が持っている基準があります:

  1. 識別子のインデックス
  2. 識別子の値

指定されたインデックス(カンマで区切られた)で指定された値を持つCSV内のすべての行を取得したいと思います。

パフォーマンスについて特別に考慮したアイデアはありますか?

33
tinkertime

プレーンな古いgrepcutを使用した最初のプロトタイプ:

grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}

これで十分に高速で適切な出力が得られれば、完了です。 :)

27
unwind

cut-またはawkベースのワンライナーの代替として、特殊な csvtool aka ocaml-csv

$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"

ドキュメントによると、エスケープ、引用などを処理します。

44

この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
40
FRV

CSVはそれほど単純ではありません。持っているデータの制限に応じて、引用符で囲まれた値(コンマや改行を含む場合がある)や引用符のエスケープについて心配する必要があるかもしれません。

したがって、データが十分に制限されている場合は、単純なコンマ分割で問題なく処理できれば、シェルスクリプトで簡単に実行できます。一方、CSVを「適切に」解析する必要がある場合、bashは最初の選択肢ではありません。代わりに、高レベルのスクリプト言語、たとえばPython with csv.reader を見てください。

12
bobince

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変数に割り当てます。一致する場合は、元の行を印刷します。

9
Hai Vu

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はほとんど 実際のプログラミング言語 ...です.

7
Nate Kohl

データに特殊文字が含まれていない状況では、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の実行後に復元されます。

4
D Bro
index=1
value=2
awk -F"," -v i=$index -v v=$value '$(i)==v' file
3
ghostdog74

sedまたはawkのソリューションはおそらく短いでしょうが、Perlの場合は次のとおりです。

Perl -F/,/ -ane 'print if $F[<INDEX>] eq "<VALUE>"`

ここで、<INDEX>は0から始まります(最初の列に0、2番目の列に1など)。

2
mob

引用符をサポートし、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を使用して必要なフィールドを取得します。