bash,提取一个csv文件的一列
如果我有一个csv文件,是否有一个快速bash的方式来打印出只有任何一列的内容? 假设每行的列数相同,但每列的内容长度不同,这是安全的。
你可以用awk来做这个。 将“$ 2”更改为所需的第n列。
awk -F "\"*,\"*" '{print $2}' textfile.csv
是。 cat mycsv.csv | cut -d ',' -f3
cat mycsv.csv | cut -d ',' -f3
将打印第3列。
在这里登陆寻找从标签分离的文件中提取。 以为我会补充。
cat textfile.tsv | cut -f2 -s
其中-f2
提取2,非零索引列或第二列。
我能够完成这个最简单的方法就是使用csvtool 。 我还有其他使用情况,以及使用csvtool,它可以适当地处理引号或分隔符,如果它们出现在列数据本身。
csvtool format '%(2)\n' input.csv
用列号replace2将有效地提取您正在查找的列数据。
这个问题的答案很多,甚至有的甚至考虑了angular落案例。 我想添加一个简单的答案,可以日常使用…你主要进入那些angular落的情况下(如已经在逗号或逗号逗号引号等)。
FS(Field Separator)是其值被空间化的variables。 所以AWK在默认情况下在任何一行的空间分割。
所以使用BEGIN(在input前执行),我们可以将这个字段设置为任何我们想要的…
awk 'BEGIN {FS = ","}; {print $3}'
上面的代码将在csv文件中打印第三列。
其他答案工作得很好,但是因为你只是使用bash shell来寻求一个解决scheme,所以你可以这样做:
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(内部字段分隔符),这是shell用来知道分隔字段(文本块)的东西。 所以说IFS =,就像说“a,b”和“ab”是一样的,如果IFS =“”(这是默认的)。 -
read -a csv_line;
– 这是说每行读一行,并创build一个数组,其中每个元素被称为“csv_line”,并发送到我们的while循环的“做”部分 -
do echo "${csv_line[0]}";done < file
– 现在我们处于“do”阶段,我们说echo数组“csv_line”的第0个元素。 这个动作在文件的每一行重复。< file
部分只是告诉while循环从哪里读取。 注意:记住,在bash中,数组是0索引的,所以第一列是第0个元素。
所以你有它,从壳中的CSV中拉出一列。 其他解决scheme可能更实用,但这是纯粹的bash。
您可以使用GNU Awk,请参阅用户指南的这篇文章 。 作为文章(2015年6月)中提出的解决scheme的改进,以下gawk命令允许在双引号字段内使用双引号; 双引号在那里连续两个双引号(“”)。 而且,这允许空字段, 但即使这样也不能处理多行字段 。 以下示例显示textfile.csv的第三列(通过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作为换行符,参见维基百科 。
如果input可能包含多行字段,则可以使用以下脚本。 请注意使用特殊string分隔输出中的logging(因为默认的分隔符换行符可能发生在logging中)。 再次,下面的例子打印textfile.csv的第三列(通过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可以输出被修改的CSV文件的内容,以便字段中的特殊字符被转换,从而可以使用通常的Unix文本处理工具来select某个列。 例如下面的代码输出第三列:
csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u
csvquote
可以用来处理任意大文件。
[dumb @ one pts] $ cat> file#首先,我们将创build一个基本的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
[愚蠢@一分] $ awk -F,'{print $ 1}'文件
一个
1
一个
1
没有完整的CSVparsing器就无法做到这一点。
一直使用这段代码,除非你指望“从stackoverflow剪切和粘贴”,否则不是“快速”。
它在循环中使用$ {##}和$ {%%}运算符而不是IFS。 它叫'err'和'die',只支持逗号,短划线和pipe道作为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