Answers:
您可以为此使用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
将打印第三列。
awk
我能够完成此操作的最简单方法是仅使用csvtool。我还有其他使用csvtool的用例,如果它们出现在列数据本身中,它可以适当地处理引号或定界符。
csvtool format '%(2)\n' input.csv
用列号替换2将有效地提取您要查找的列数据。
cat input.csv | csvtool formath '%(2)\n' -
注意以下几点:我知道这里的cat是没有用的,但对于通常会导出csv的任何命令都使用cat。
format '%(2)\n'
命令无法确定一个字段的结尾。(csvtool 1.4.2)
csvtool
似乎要求使用-
输入作为从stdin读取的文件名。
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
这个问题的许多答案都很棒,有些甚至已经探究了极端情况。我想添加一个简单的答案,该答案可以每天使用...在这里,您大多会遇到那些极端的情况(例如,用逗号或引号引起来的逗号等)。
FS(字段分隔符)是变量,其值默认为空格。因此,默认情况下,awk会在空格处拆分任何行。
因此,使用BEGIN(在输入之前执行),我们可以将此字段设置为所需的任何内容...
awk 'BEGIN {FS = ","}; {print $3}'
上面的代码将在csv文件中打印第3列。
其他答案效果很好,但是由于您仅使用bash shell寻求解决方案,因此您可以执行以下操作:
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”与“ a b”相同就是IFS =“”(默认情况下就是这样)。
read -a csv_line;
-也就是说每行一次读取,并创建一个数组,其中每个元素称为“ csv_line”,并将其发送到while循环的“ do”部分
do echo "${csv_line[0]}";done < file
-现在我们处于“ do”阶段,我们说的是echo数组“ csv_line”的第0个元素。在文件的每一行上重复此操作。该< file
部分只是告诉while循环从何处读取。注意:请记住,在bash中,数组的索引为0,因此第一列为第0个元素。
这样就可以在外壳中从CSV中拉出一列。其他解决方案可能更实用,但这是纯粹的bash。
您可以使用GNU Awk,请参阅用户指南中的这篇文章。作为对本文(2015年6月)中提出的解决方案的改进,以下gawk命令允许在双引号字段内使用双引号。双引号在那里用两个连续的双引号(“”)标记。此外,这允许空字段,但即使这样也不能处理多行字段。以下示例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作为换行符,请参见Wikipedia。
如果输入中可能包含多行字段,则可以使用以下脚本。请注意,使用特殊字符串分隔输出中的记录(因为默认分隔符换行符可能会在记录中出现)。同样,以下示例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可以输出修改后的CSV文件的内容,以便转换字段中的特殊字符,以便可以使用常规的Unix文本处理工具来选择某些列。例如,以下代码输出第三列:
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代表Field,d代表定界符
运行以上命令将产生以下输出。
输出量
Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28
仅获取第二列:
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 ...]
首先,我们将创建一个基本的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
我想知道为什么到目前为止没有答案提到csvkit。
csvkit是一套命令行工具,用于转换为CSV并使用CSV
我专门将其用于csv数据管理,到目前为止,我还没有发现使用cvskit无法解决的问题。
要从cvs文件中提取一列或多列,您可以使用csvcut
工具箱中的实用程序。要提取第二列,请使用以下命令:
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
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
将打印2
而不是2,3,4,5
。