web-dev-qa-db-ja.com

csvファイルの1つの列を抽出する方法

Csvファイルがある場合、単一の列のみの内容を印刷する簡単なbash方法はありますか?各行の列数は同じであると想定しても安全ですが、各列のコンテンツの長さは異なります。

85
user788171

これにはawkを使用できます。 '$ 2'を必要なn番目の列に変更します。

awk -F "\"*,\"*" '{print $2}' textfile.csv
98

はい。 cat mycsv.csv | cut -d ',' -f3は3列目を出力します。

71
madrag

これを実現できる最も簡単な方法は、 csvtool を使用することです。 csvtoolを使用する他のユースケースもあり、列データ自体に引用符または区切り文字が含まれている場合、それらを適切に処理できます。

csvtool format '%(2)\n' input.csv

2を列番号に置き換えると、探している列データが効果的に抽出されます。

29
Samar

タブ区切りファイルから抽出するためにここに着陸しました。私が追加すると思いました。

cat textfile.tsv | cut -f2 -s

-f2は、2の非ゼロインデックス列、または2番目の列を抽出します。

13
cevaris

この質問に対する多くの回答は素晴らしいものであり、一部のケースではコーナーケースについても検討しています。私は日常的に使用できる簡単な答えを追加したいと思います...ほとんどの場合、それらの角の場合になります(エスケープされたコンマや引用符でのコンマなど)。

FS(フィールドセパレーター)は、値がスペースにフォールトされる変数です。そのため、awkはデフォルトで任意の行のスペースで分割します。

したがって、BEGIN(入力を取得する前に実行)を使用して、このフィールドを必要なものに設定できます...

awk 'BEGIN {FS = ","}; {print $3}'

上記のコードは、csvファイルの3列目を印刷します。

6
router

他の答えはうまくいきますが、bashシェルだけを使用して解決策を求めたので、これを行うことができます。

AirBoxOmega:~ d$ cat > file #First we'll create a basic CSV
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10

そして、次のように列(この例の最初の列)を引き出すことができます。

AirBoxOmega:~ d$ while IFS=, read -a csv_line;do echo "${csv_line[0]}";done < file
a
1
a
1
a
1
a
1
a
1
a
1

そのため、ここでいくつかのことが行われています。

  • while IFS=,-これは、フィールド(テキストのブロック)を区切るものを知るためにシェルが使用するIFS(Internal Field Separator)としてコンマを使用することを言っています。つまり、IFS =と言うことは、「a、b」は「a b」と同じようなことです。IFS= ""の場合(これはデフォルトです)

  • read -a csv_line;-これは、各行を1つずつ読み取り、各要素が「csv_line」と呼ばれる配列を作成し、whileループの「do」セクションに送信することを意味します

  • do echo "${csv_line[0]}";done < file-「do」フェーズにあり、配列「csv_line」の0番目の要素をエコーし​​ていると言います。このアクションは、ファイルのすべての行で繰り返されます。 < file部分は、whileループにどこから読み込むかを伝えるだけです。注:bashでは、配列のインデックスは0であるため、最初の列は0番目の要素です。

これで、シェルのCSVから列を引き出しました。他のソリューションはおそらくより実用的ですが、これは純粋なbashです。

5
drldcsta

GNU Awkを使用できます。 ユーザーガイドのこの記事 を参照してください。記事(2015年6月)で提示されたソリューションの改善として、次のgawkコマンドは二重引用符付きフィールド内の二重引用符を許可します。二重引用符は、2つの連続する二重引用符( "")でマークされます。さらに、これは空のフィールドを許可しますが、これでも複数行フィールドを処理できません。次の例では、textfile.csvの3番目の列(c=3経由)を出力します。

#!/bin/bash
gawk -- '
BEGIN{
    FPAT="([^,\"]*)|(\"((\"\")*[^\"]*)*\")"
}
{
    if (substr($c, 1, 1) == "\"") {
        $c = substr($c, 2, length($c) - 2) # Get the text within the two quotes
        gsub("\"\"", "\"", $c)  # Normalize double quotes
    }
    print $c
}
' c=3 < <(dos2unix <textfile.csv)

dos2unixを使用して、可能性のあるDOSスタイルの改行(CRLFすなわち "\ r\n")およびUTF-16エンコーディング(バイトオーダーマーク付き)をそれぞれ "\ n"およびUTF-8(バイトオーダーマークなし)に変換することに注意してください。標準CSVファイルはCRLFを改行として使用します。 Wikipedia を参照してください。

入力に複数行のフィールドが含まれる場合は、次のスクリプトを使用できます。出力でレコードを分離するための特別な文字列の使用に注意してください(デフォルトの区切り改行はレコード内で発生する可能性があるため) 。繰り返しますが、次の例では、textfile.csvの3番目の列(c=3経由)を出力します。

#!/bin/bash
gawk -- '
BEGIN{
    RS="\0" # Read the whole input file as one record;
    # assume there is no null character in input.
    FS="" # Suppose this setting eases internal splitting work.
    ORS="\n####\n" # Use a special output separator to show borders of a record.
}
{
    nof=patsplit($0, a, /([^,"\n]*)|("(("")*[^"]*)*")/, seps)
    field=0;
    for (i=1; i<=nof; i++){
        field++
        if (field==c) {
            if (substr(a[i], 1, 1) == "\"") {
                a[i] = substr(a[i], 2, length(a[i]) - 2) # Get the text within 
                # the two quotes.
                gsub(/""/, "\"", a[i])  # Normalize double quotes.
            }
            print a[i]
        }
        if (seps[i]!=",") field=0
    }
}
' c=3 < <(dos2unix <textfile.csv)

問題に対する別のアプローチがあります。 csvquote は、通常のUnixテキスト処理ツールを使用して特定の列を選択できるように、フィールド内の特殊文字が変換されるように変更されたCSVファイルの内容を出力できます。たとえば、次のコードは3番目の列を出力します。

csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u

csvquoteを使用して、任意の大きなファイルを処理できます。

5
jarno

[dumb @ one pts] $ cat> file#最初に基本的なCSVを作成します
a、b、c、d、e、f、g、h、i、k
1,2,3,4,5,6,7,8,9,10
a、b、c、d、e、f、g、h、i、k
1,2,3,4,5,6,7,8,9,10

[dumb @ one pts] $ awk -F、 '{print $ 1}'ファイル
a
1
a
1

4
Raj Velayudhan

cut/awkと祈りではなく、適切なCSV解析が必要でした。 csvtoolのないMacでこれを試していますが、MacにはRubyが付属しているので、次のことができます。

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | Ruby
3
Darth Egregious

完全なCSVパーサーなしでは実行できません。

1
Peter Krumins
csvtool col 2 file.csv 

ここで、2は興味のある列です

あなたもできる

csvtool col 1,2 file.csv 

複数の列を行う

1
exussum

これまでのところ、csvkitについての回答がなかったのはなぜですか。

csvkitは、CSVに変換して操作するためのコマンドラインツールのスイートです。

csvkitドキュメント

私はそれをcsvデータ管理専用に使用していますが、今のところcvskitを使用して解決できない問題は見つかりませんでした。

Cvsファイルから1つ以上の列を抽出するには、ツールボックスの一部であるcsvcutユーティリティを使用できます。 2番目の列を抽出するには、次のコマンドを使用します。

csvcut -c 2 filename_in.csv > filename_out.csv 

csvcutリファレンスページ

Csvの文字列が引用符で囲まれている場合は、qオプションを使用して引用文字を追加します。

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

pip install csvkitまたはSudo apt install csvkitを使用してインストールします。

1
Soundbytes

最も簡単なのは csvkit を使用することだと思います:

2番目の列を取得します:csvcut -c 2 file.csv

ただし、 csvtool もあり、おそらく他にも多くのcsv bashツールがあります:

Sudo apt-get install csvtool(Debianベースのシステム用)

これにより、最初の行に「ID」が含まれる列が返されます。 csvtool namedcol ID csv_file.csv

これは4番目の行を返します:csvtool col 4 csv_file.csv

ヘッダー行をドロップする場合:

csvtool col 4 csv_file.csv | sed '1d'

0
wordsforthewise

これは2列のcsvファイルの例です

myTooth.csv

Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom

最初の列を取得するには、次を使用します。

cut -d, -f1 myTooth.csv

fはフィールドを表し、dは区切り文字を表します

上記のコマンドを実行すると、次の出力が生成されます。

出力

Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28

2列目のみを取得するには:

cut -d, -f2 myTooth.csv

そして、ここに出力がありますOutput

Tooth
wisdom
canine
canine
wisdom
incisor

別のユースケース:

Csv入力ファイルには10個の列が含まれており、カンマを区切り文字として使用して、列2〜5および列8が必要です。

cutは、-f(「フィールド」を意味する)を使用して列を指定し、-d(「デリミター」を意味する)を使用して区切り文字を指定します。一部のファイルでは列を区切るためにスペース、タブ、またはコロンを使用する場合があるため、後者を指定する必要があります。

cut -f 2-5,8 -d , myvalues.csv

cutはコマンドユーティリティであり、ここにいくつかの例を示します。

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]
0
Stryker

しばらくこのコードを使用していましたが、「stackoverflowからのカットアンドペースト」をカウントしない限り、「クイック」ではありません。

IFSの代わりにループで$ {##}および$ {%%}演算子を使用します。 'err'と 'die'を呼び出し、SEP charsとしてコンマ、ダッシュ、およびパイプのみをサポートします(必要なのはこれだけです)。

err()  { echo "${0##*/}: Error:" "[email protected]" >&2; }
die()  { err "[email protected]"; exit 1; }

# Return Nth field in a csv string, fields numbered starting with 1
csv_fldN() { fldN , "$1" "$2"; }

# Return Nth field in string of fields separated
# by SEP, fields numbered starting with 1
fldN() {
        local me="fldN: "
        local sep="$1"
        local fldnum="$2"
        local vals="$3"
        case "$sep" in
                -|,|\|) ;;
                *) die "$me: arg1 sep: unsupported separator '$sep'" ;;
        esac
        case "$fldnum" in
                [0-9]*) [ "$fldnum" -gt 0 ] || { err "$me: arg2 fldnum=$fldnum must be number greater or equal to 0."; return 1; } ;;
                *) { err "$me: arg2 fldnum=$fldnum must be number"; return 1;} ;;
        esac
        [ -z "$vals" ] && err "$me: missing arg2 vals: list of '$sep' separated values" && return 1
        fldnum=$(($fldnum - 1))
        while [ $fldnum -gt 0 ] ; do
                vals="${vals#*$sep}"
                fldnum=$(($fldnum - 1))
        done
        echo ${vals%%$sep*}
}

例:

$ CSVLINE="example,fields with whitespace,field3"
$ $ for fno in $(seq 3); do echo field$fno: $(csv_fldN $fno "$CSVLINE");  done
field1: example
field2: fields with whitespace
field3: field3
0
qneill

Whileループも使用できます

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv
0
K. Sopheak