web-dev-qa-db-ja.com

unixコマンドを使用してcsvファイルから4番目の列を抽出する

次のような形式のcsvファイルがあります。

"col1","col2","col3","col4"
"1","text1","<p>big
      html
     text</p>
","4th column"
"2","text2","<p>big2
      html2
     text2</p>
","4th column2"

4列目を使って抽出したい。私はawkがこれに最適なツールだと思います(私が間違っていたら知らせてください)。私はこれを試しました

awk -F, '{print $4}' myFile.csv 

しかし失敗します。 3列目は複数行なので。 awkまたは他のUNIXコマンドを使用して4番目の列を抽出するにはどうすればよいですか。実際のファイルが大きい(> 2GB)ので、効率的なソリューションを探しています

6
agstudy

更新:

実際、はるかに簡単な方法は、gawkにレコード区切りを設定することです。

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4}' myFile.csv
"col4
"4th column
"4th column2

ただし、これにより、各列の末尾から末尾の"が削除されます。これを修正するには、自分で印刷できます。

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4"\""}' myFile.csv
"col4"
"4th column"
"4th column2"

引用符がまったく不要な場合は、フィールド区切り記号を","に設定できます。

$ gawk 'BEGIN{RS="\"\n"; FS="\",\""}{print $3}' myFile.csv
col3
4th column
4th column2

私が考えることができる唯一の方法 これを行う1つの方法は、最初にファイルを変更してから解析することです。あなたの例では、2つのレコードを実際に区切る改行は、常に"に従っています:

"col1","col2","col3","col4"   <-- here 
1,"text1","<p>big             <-- no "

これがファイル全体に当てはまる場合は、"の直後ではないすべての改行をプレースホルダーに置き換えることができるため、すべてを1行で記述できます。その後、gawkを使用して通常どおり解析し、最後にプレースホルダーを改行で再度置換できます。文字列&%&は、ファイルに存在する可能性が低いため、プレースホルダーとして使用します。

$ Perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv | awk -F, '{print $4}'
"col4"
"4th column"
"4th column2"

Perl-pフラグは、print each line of the input fileで指定されたスクリプトを適用した後の-eを意味します。次に、3つの置換(s/foo/bar/)コマンドがあります。

  • s/"\s*\n/"&%&/"の後に0個以上の空白文字(\s*)と改行文字(\n)が続くものを検索します。それを"&%&に置き換えます。引用符は形式を維持するために追加されており、&%&はランダムなプレースホルダーです。ファイルに表示されないものでもかまいません。

  • s/\n//g;:実際の改行がプレースホルダーに置き換えられたため、このレコードの残りの改行をすべて安全に削除できるようになりました。つまり、現在のレコードのすべての行が現在の行に連結されています。

  • s/&%&/\n/:プレースホルダーを通常の新しい行に戻します。

コマンドの出力を理解するには、gawkなしで実行します。

$ Perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv 
"col1","col2","col3","col4"
1,"text1","<p>big      html     text</p>","4th column"
2,"text2","<p>big2      html2     text2</p>","4th column2"

これで、長いレコードが1行になり、これはgawkに最適な食べ物です。

Perlで直接行うこともできます。

Perl -ne '$/="\"\n"; chomp;@a=split(/,/);print "$a[3]\"\n"' myFile.csv
"col4"
"4th column"
"4th column2"

これはもう少しPerlマジックを使用しています。 $/special変数は、入力レコードの区切り記号です。 "\nに設定することで、\nではなく"\n"でのみ行を分割するようにPerlに指示し、各レコードが単一行として扱われるようにします。それが完了すると、chompは改行を行末から削除し(後で印刷するため)、splitは各レコードを分割し(,で)、配列に保存します@a。最後に、4番目の列である配列の4番目の要素(配列には0から番号が付けられるため、$a[3])を出力します。

さらに魔法をかけるには、自動吐き出し(-a)をオンにして、コンマ(F",")で分割します。これにより、各レコードが特別な@F配列に分割され、配列の4番目の要素を出力できます。

$ Perl -F"," -ane '$/="\"\n";chomp;print "$F[3]"' myFile.csv
"col4"
"4th column"
"4th column2"
10
terdon

戦闘テスト済みのCSV解析モジュールを使用することをお勧めします。例えば:

Perl -MText::CSV -E '
    $csv = Text::CSV->new({binary=>1}); 
    while ($row = $csv->getline(STDIN)) {say $row->[3]}
' < file.csv
col4
4th column
4th column2

またはこれは同じ結果を生成します:

Ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' file.csv
6
glenn jackman

Python:

python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" myfile.csv

リストを介してファイルのコンテンツをメモリにロードする上記のアプローチとは異なり、一度に1行ずつファイルを反復処理する大きなファイルのメモリ保護ソリューション

#!/usr/bin/env python
import sys
import csv
with open(sys.argv[1]) as f:
  for row in csv.reader(f):
    print(row[3])

すべてのソリューションのテスト結果:

OS:Ubuntu 12.04

公開CSVデータのダウンロード: http://seanlahman.com/baseball-archive/statistics/

バージョンの詳細

root@ubuntu:~# python --version
Python 2.7.3
root@ubuntu:~# Ruby --version
Ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
root@ubuntu:~# Perl --version

This is Perl 5, version 14, Subversion 2 (v5.14.2) built for i686-linux-gnu-thread-multi-64int

timeの結果

root@ubuntu:~# time python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" Master.csv > /tmp/python

real    0m1.112s
user    0m0.056s
sys     0m0.316s
root@ubuntu:~# time Ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' Master.csv > /tmp/Ruby

real    0m24.582s
user    0m23.397s
sys     0m0.448s
root@ubuntu:~# time Perl -MText::CSV -E '
>     $csv = Text::CSV->new({binary=>1});
>     while ($row = $csv->getline(STDIN)) {say $row->[3]}
> ' < Master.csv > /tmp/Perl

real    0m7.049s
user    0m5.876s
sys     0m0.468s
5
Rahul Patil

uNIXスタイルの場合、「\ n」で終了する行

tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","} !(NR % 4)'

一部のフィールドはmulti-lines _tr -d "\n"_はすべての改行文字を削除し、「、」で区切られた値のストリームを作成します。 awkは、行区切り文字として「、」を使用し、(行番号モジュロ4)が0になるたびに出力するように指示されます。

これは、4番目のフィールドが(サンプルのように)最後のフィールドである場合にのみ機能します。そうでない場合:

tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","; last=12} (++c == 4) (c == last) {c=0}'

行をカウントし、countが4の場合に行を出力し、最後のフィールドに達したときにカウントをリセットします。

2
Emmanuel

以下を試してください:

while IFS=',' read -r a1 a2 a3 a4 
do 
echo "$a4" >> urname.csv
done < input.csv
1
user3116123

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

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

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

1
Samar

を使用してCSVの4番目の要素を取得します。

cut -d , -f 4 myFile.csv  

次のコマンドでファイルに保存します。

cut -d , -f 4 myFile.csv | cat >> my4thEltsFile.csv
0
Choleski Louis