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


111

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

回答:


135

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

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

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'2代わりに印刷されます2,3,4,5
Igor Mikushkin 2015年

:あなたは、WindowsのGNUツールを使用して幸運な男であれば、以下のように、あなたは@IgorMikushkinと同じCOMANDを実行することができますgawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
私は、これは、つまりカンマを含む文字列があるときに失敗したと思う...,"string,string",...
硝酸ナトリウム

最初と最後の列については、これにはいくつかの欠陥があると思います。最初の列は始まり"、最後は次のように終わります"
BigTailWolf 2018年

一部のプログラムは、区切り文字が異なるCSVファイルを返すため、それに応じて正規表現を変更する必要があります。セミコロン区切り文字の例: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

はい。cat mycsv.csv | cut -d ',' -f33列目を印刷します。


8
列2にコンマが含まれていない限り、その場合は列2の後半が取得されます。ケース<col1>、 "3,000"、<col2>。私の答えはその問題に関してはあまりよくありません。だから、困惑しないでください。
シンセサイザ

@synthesizerpatel使用することに同意しますawk
MattSizzle 2013年

1
彼のCSVファイルに、異なる値を区別するための二重引用符が含まれていることはわかりません。最も適切なソリューションを評価できるように、彼が入力ファイルを提供することをお勧めします。
Idriss Neumann

50

私がこれを行うことができた最も簡単な方法は、単にcsvtoolを使用することでした。csvtoolを使用する他のユースケースもあり、列データ自体に表示される場合、引用符または区切り記号を適切に処理できます。

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

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


14
これは受け入れられる答えになるはずです。このツールは、CSVファイルの扱い方を知っており、コンマをフィールド区切り文字として扱うことはできません。2列目を抽出するには、「csvtool col 2 input.csv」
Vladislavs Dovgalecs 2016年

3
ただのヘッドアップ...標準入力でcsvtoolを使用する場合(例csvは別のコマンドから取得されます)は、次のようなものcat input.csv | csvtool formath '%(2)\n' -です。
レッドネック将軍2018年

複数行のフィールドがある場合、format '%(2)\n'コマンドは1つのフィールドがどこで終了するかを認識できませんでした。(csvtool 1.4.2)
jarno

1
新しいバージョンのcsvtool-、stdinから読み取るために入力ファイル名としてを使用する必要があるようです。
Connor Clark、

@GeneralRedneck猫を使用する理由 そしてそれはformathではなくformatですcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

14

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

cat textfile.tsv | cut -f2 -s

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


単純すぎますが、他の例よりも簡単に適応できます。ありがとう!
Nick Jennings

6
つべこべ、しかしすることはcat不要である:< textfile.tsv cut -f2 -s
アン・バン・ロッサム

8

この質問に対する多くの回答は素晴らしいものであり、いくつかはコーナーケースを調査したものさえあります。日常的に使用できる簡単な回答を追加したいと思います...エスケープされたコンマや引用符で囲まれたコンマなど、主にこれらのコーナーケースに陥ります。

FS(Field Separator)は、値がスペースに変換される変数です。したがって、awkはデフォルトで任意の行のスペースで分割します。

したがって、BEGIN(入力を受け取る前に実行)を使用して、このフィールドを任意の値に設定できます...

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

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


1
私はこれを試しましたが、引用されたフィールド内のコンマをまだ考慮しています。
Daniel C. Sobral

5

他の答えはうまくいきますが、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

GNU Awkを使用できます。ユーザーガイドのこの記事を参照してください。記事(2015年6月)に記載されているソリューションの改善点として、次のgawkコマンドでは二重引用符で囲まれたフィールド内で二重引用符を使用できます。二重引用符は、2つの連続した二重引用符( "")でマークされています。さらに、これは空のフィールドを許可しますが、これでも複数行のフィールドを処理することはできません。次の例では、c=3textfile.csvの(を介して)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を使用します。ウィキペディアを参照してください

入力に複数行フィールドが含まれている可能性がある場合は、次のスクリプトを使用できます。出力でレコードを区切るために特別な文字列を使用していることに注意してください(デフォルトのセパレーター改行がレコード内で発生する可能性があるため)。繰り返しになりますが、次の例では、c=3textfile.csvの(を介して)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

これは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

そしてこれが 出力です

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 ...]

4

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

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

4

まず、基本的なCSVを作成します

[dumb@one pts]$ cat > file 
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}' file  
a  
1  
a  
1

3
csvtool col 2 file.csv 

2は興味のある列です

あなたもできる

csvtool col 1,2 file.csv 

複数の列を行うには


3

私は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'


2

なぜこれまで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ます。



0

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

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

err()  { echo "${0##*/}: Error:" "$@" >&2; }
die()  { err "$@"; 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

whileループも使用できます

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

        echo Name: "$name"
done<itemlst.csv

このコードは、Shellcheck警告SC2034を生成します。警告を回避する方法を探すとき、検索はこの質問を最初の結果として返します。
jww
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.