如何提取csv文件的一列

时间:2013-10-26 02:32:41

标签: bash csv extract

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

17 个答案:

答案 0 :(得分:98)

你可以使用awk。将“$ 2”更改为您想要的第n列。

awk -F "\"*,\"*" '{print $2}' textfile.csv

答案 1 :(得分:71)

是肯定的。 cat mycsv.csv | cut -d ',' -f3将打印第3列。

答案 2 :(得分:27)

我能够完成这项工作的最简单方法就是使用csvtool。我还有其他用例也可以使用csvtool,如果它们出现在列数据本身内,它可以适当地处理引号或分隔符。

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

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

答案 3 :(得分:13)

在这里登陆,希望从制表符分隔文件中提取。以为我会补充一下。

cat textfile.tsv | cut -f2 -s

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

答案 4 :(得分:6)

这个问题的很多答案都很棒,有些人甚至研究过这些问题。 我想添加一个可以日常使用的简单答案......你大多数时候都会遇到这些极端情况(例如在引号等中使用逗号或逗号进行转义)。

  

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

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

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

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

答案 5 :(得分:5)

您可以使用GNU Awk,请参阅this article of the user guide。 作为对文章(2015年6月)中提出的解决方案的改进,以下gawk命令允许双引号字段内的双引号;双引号用两个连续的双引号(“”)标记。此外,这允许空字段,但即使这样也无法处理多行字段。以下示例打印textfile.csv的第3列(通过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作为换行符,请参阅Wikipedia

如果输入可能包含多行字段,则可以使用以下脚本。请注意使用特殊字符串分隔输出中的记录(因为默认分隔符换行符可能出现在记录中)。同样,以下示例打印textfile.csv的第3列(通过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文本处理工具来选择某个列。例如,以下代码输出第三列:

csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u

csvquote可用于处理任意大文件。

答案 6 :(得分: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”部分< / p>

  • do echo "${csv_line[0]}";done < file - 现在我们处于“做”阶段,我们说的是回显数组“csv_line”的第0个元素。在文件的每一行上重复此操作。 < file部分只是告诉while循环从哪里读取。注意:记住,在bash中,数组是0索引的,因此第一列是第0个元素。

所以你有它,从shell中的CSV中拉出一列。其他解决方案可能更实用,但这个是纯粹的bash。

答案 7 :(得分:4)

[dumb @ one pts] $ cat&gt;文件#First我们将创建一个基本的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

[dumb @ one pts] $ awk -F,&#39; {print $ 1}&#39;文件
一个
1
一个
1

答案 8 :(得分:3)

我需要正确的CSV解析,而不是cut / awk和祈祷。我在没有csvtool的Mac上尝试这个,但mac确实带有ruby,所以你可以这样做:

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby

答案 9 :(得分:1)

如果没有完整的CSV解析器,则无法执行此操作。

答案 10 :(得分:1)

csvtool col 2 file.csv 

其中2是您感兴趣的列

您也可以

csvtool col 1,2 file.csv 

要做多列

答案 11 :(得分:1)

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

  

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

csvkit documentation

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

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

csvcut -c 2 filename_in.csv > filename_out.csv 

csvcut reference page

如果csv中的字符串用引号引起来,请使用q选项添加引号字符:

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

使用pip install csvkitsudo apt install csvkit安装。

答案 12 :(得分:1)

我认为最简单的方法是使用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'

答案 13 :(得分: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

答案 14 :(得分:0)

这是一个包含两列的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

仅获取第二列:

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

答案 15 :(得分:0)

您还可以使用while循环

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv

答案 16 :(得分:0)

使用awk的简单解决方案。代替“colNum”,输入您需要打印的列数。

cat fileName.csv | awk -F ";" '{ print $colNum }'