如何提取CSV文件的一列


111

如果我有一个csv文件,是否有一种快速的bash方法可以只打印出任何一列的内容?可以安全地假设每一行具有相同的列数,但是每一列的内容将具有不同的长度。

Answers:


136

您可以为此使用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
伊戈尔·米库什金

如果您很幸运,可以在Windows中使用GNU工具,则可以执行与@IgorMikushkin相同的命令,如下所示:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
我认为如果字符串包含逗号(例如...,"string,string",...
硝酸钠)

我认为对于第一个和最后一个摘要,这将存在一些缺陷。第一列将开始,"最后一列将结束"
BigTailWolf

某些程序返回带有不同定界符的CSV文件,因此可能需要相应地更改正则表达式。分号分隔符的示例: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

是。cat mycsv.csv | cut -d ',' -f3将打印第三列。


8
除非第二列包含逗号,否则您将获得第二列的后半部分。以<col1>,“ 3,000”,<col2>为例。关于这个问题,我的回答并不是更好。因此,不要被淘汰。
synthesizerpatel

@synthesizerpatel我同意更好地使用awk
MattSizzle 2013年

1
我们不确定他的CSV文件中是否包含双引号来区分不同的值。他最好提供一个输入文件,以便我们评估最合适的解决方案。
伊德里斯·诺伊曼

51

我能够完成此操作的最简单方法是仅使用csvtool。我还有其他使用csvtool的用例,如果它们出现在列数据本身中,它可以适当地处理引号或定界符。

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

用列号替换2将有效地提取您要查找的列数据。


14
这应该是公认的答案。该工具知道如何处理CSV文件,而不仅仅是将逗号视为字段分隔符。要提取第二列“ csvtool col 2 input.csv”
Vladislavs Dovgalecs 16-10-28

3
请注意...如果要在标准输入中使用csvtool(示例csv来自其他命令),则应cat input.csv | csvtool formath '%(2)\n' -注意以下几点:我知道这里的cat是没有用的,但对于通常会导出csv的任何命令都使用cat。
乡下人将军,

它有多行字段,该format '%(2)\n'命令无法确定一个字段的结尾。(csvtool 1.4.2)
jarno,

1
较新版本的csvtool似乎要求使用-输入作为从stdin读取的文件名。
康纳·克拉克

@GeneralRedneck为什么要用猫?它的格式不是formathcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

14

登陆此处以寻求从制表符分隔的文件中提取信息。以为我会补充。

cat textfile.tsv | cut -f2 -s

其中-f2提取2,非零索引列或第二列。


简单点也很重要,并且比其他示例更容易适应。谢谢!
尼克·詹宁斯

6
细细挑选,但cat不是必须的:< textfile.tsv cut -f2 -s
安妮·范·罗苏姆

8

这个问题的许多答案都很棒,有些甚至已经探究了极端情况。我想添加一个简单的答案,该答案可以每天使用...在这里,您大多会遇到那些极端的情况(例如,用逗号或引号引起来的逗号等)。

FS(字段分隔符)是变量,其值默认为空格。因此,默认情况下,awk会在空格处拆分任何行。

因此,使用BEGIN(在输入之前执行),我们可以将此字段设置为所需的任何内容...

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

上面的代码将在csv文件中打印第3列。


1
我已经尝试过了,但它仍然考虑带引号的字段内的逗号。
Daniel C. Sobral

5

其他答案效果很好,但是由于您仅使用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。


5

您可以使用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 可用于处理任意大文件。


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

4

我需要适当的CSV解析,而不是cut/ awk和祈祷。我正在没有的Mac上尝试此操作csvtool,但是Mac 确实随附了ruby,因此您可以执行以下操作:

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

获取第二列: csvcut -c 2 file.csv

但是,还有csvtool,可能还有许多其他的csv bash工具:

sudo apt-get install csvtool (对于基于Debian的系统)

这将返回其中第一行具有“ ID”的列。 csvtool namedcol ID csv_file.csv

这将返回第四行: csvtool col 4 csv_file.csv

如果要删除标题行:

csvtool col 4 csv_file.csv | sed '1d'


2

我想知道为什么到目前为止没有答案提到csvkit。

csvkit是一套命令行工具,用于转换为CSV并使用CSV

csvkit文档

我专门将其用于csv数据管理,到目前为止,我还没有发现使用cvskit无法解决的问题。

要从cvs文件中提取一列或多列,您可以使用csvcut工具箱中的实用程序。要提取第二列,请使用以下命令:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.