csvファイルがある場合、単一の列のコンテンツのみを出力するbashの簡単な方法はありますか?各行の列数は同じであると想定しても安全ですが、各列の内容は異なる長さになります。
csvファイルがある場合、単一の列のコンテンツのみを出力するbashの簡単な方法はありますか?各行の列数は同じであると想定しても安全ですが、各列の内容は異なる長さになります。
回答:
これにはawkを使用できます。'$ 2'を必要なn番目の列に変更します。
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
、最後は次のように終わります"
awk -F "\"*;\"*" '{print $2}' textfile.csv
はい。cat mycsv.csv | cut -d ',' -f3
3列目を印刷します。
awk
私がこれを行うことができた最も簡単な方法は、単にcsvtoolを使用することでした。csvtoolを使用する他のユースケースもあり、列データ自体に表示される場合、引用符または区切り記号を適切に処理できます。
csvtool format '%(2)\n' input.csv
2を列番号に置き換えると、探している列データを効率的に抽出できます。
cat input.csv | csvtool formath '%(2)\n' -
です。
format '%(2)\n'
コマンドは1つのフィールドがどこで終了するかを認識できませんでした。(csvtool 1.4.2)
csvtool
は-
、stdinから読み取るために入力ファイル名としてを使用する必要があるようです。
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
タブで区切られたファイルから抽出するためにここに着陸しました。追加すると思いました。
cat textfile.tsv | cut -f2 -s
where -f2
は、2の非ゼロのインデックス付き列、または2番目の列を抽出します。
cat
不要である:< textfile.tsv cut -f2 -s
この質問に対する多くの回答は素晴らしいものであり、いくつかはコーナーケースを調査したものさえあります。日常的に使用できる簡単な回答を追加したいと思います...エスケープされたコンマや引用符で囲まれたコンマなど、主にこれらのコーナーケースに陥ります。
FS(Field Separator)は、値がスペースに変換される変数です。したがって、awkはデフォルトで任意の行のスペースで分割します。
したがって、BEGIN(入力を受け取る前に実行)を使用して、このフィールドを任意の値に設定できます...
awk 'BEGIN {FS = ","}; {print $3}'
上記のコードは、csvファイルの3番目の列を出力します。
他の答えはうまくいきますが、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です。
GNU Awkを使用できます。ユーザーガイドのこの記事を参照してください。記事(2015年6月)に記載されているソリューションの改善点として、次のgawkコマンドでは二重引用符で囲まれたフィールド内で二重引用符を使用できます。二重引用符は、2つの連続した二重引用符( "")でマークされています。さらに、これは空のフィールドを許可しますが、これでも複数行のフィールドを処理することはできません。次の例では、c=3
textfile.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=3
textfile.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
任意の大きなファイルを処理するために使用できます。
これは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 ...]
cut
/ awk
と祈りではなく、適切なCSV解析が必要でした。なしのMacでこれを試していますが、Macにcsvtool
はルビーが付属しているため、次のことができます。
echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
私は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'
なぜこれまでcsvkitについて言及した回答がないのでしょうか。
csvkitは、CSVに変換して操作するためのコマンドラインツールのスイートです。
私はcsvデータ管理専用に使用しており、これまでcvskitを使用して解決できない問題を発見していません。
cvsファイルから1つ以上の列を抽出するにcsvcut
は、ツールボックスの一部であるユーティリティを使用できます。2番目の列を抽出するには、次のコマンドを使用します。
csvcut -c 2 filename_in.csv > filename_out.csv
csv内の文字列が引用符で囲まれているq
場合は、オプションで引用符文字を追加します。
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
pip install csvkit
またはでインストールしsudo apt install csvkit
ます。
このコードをしばらく使用していて、「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
whileループも使用できます
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
の2
代わりに印刷されます2,3,4,5
。