如何在Linux命令行中解析CSV文件?

40

我该如何在Linux命令行上解析CSV文件?

例如:

csvparse -c 2,5,6 filename
从所有行中提取第2、5和6列的字段。它应该能够处理CSV文件格式:https://www.rfc-editor.org/rfc/rfc4180,这意味着适当引用字段并转义内部引号,因此对于具有3个字段的示例行:
field1,"field, number ""2"", has inner quotes and a comma",field3

这样,如果我请求上一行的第2个字段,我会得到:

field, number "2", has inner quotes and a comma

我知道有许多解决方案,比如Perl、Awk等,但我想要一个本地的Bash命令行工具,不需要我调用其他脚本环境或编写任何额外的代码!


3
你能否说明为什么你不想使用awk/Perl等工具,因为这些工具非常适合这个任务? - Brian Agnew
4
我不想编写任何脚本,而是希望使用一些预先打包好的工具来完成任务 :-) (就像我每次想使用排序或grep工具时都不会自己编写)。我意识到我所要求的功能可能比普通的shell工具稍微不太通用,但仍然会非常有用 - 因此提出了这个问题。 - Joel
2
我会预计在Bash中执行这种操作会非常慢。 AWK或cut是这项工作的正确工具。 - RobS
5
“cut”并不能很好地处理包含定界符的引用字符串,而这种情况在CVS文件中很常见(例如来自电子表格的导出文件)。 - Jonathan Hartley
2
另一个问题是CSV中的多行字段。 - ignis
显示剩余3条评论
12个回答

26

csvtool非常好用。在Debian/Ubuntu中可以使用(apt-get install csvtool)进行安装。例如:

csvtool namedcol Account,Cost input.csv > output.csv

请查看CSVTool手册页面了解使用技巧。


10
csvtool在写入标准输出之前,会将整个输出构建在内存中...显然没有任何流式处理。 - Pablo Lalloni
3
我也尝试了一下。一开始看起来很有前途,但如果你的CSV大于约100MB,它会因为堆栈溢出而崩溃。 - Michael
4
刚尝试使用 csvtool,五年过去了(现在是2017年),它仍然没有流式支持,导致处理一个110MB的CSV文件时出现堆栈溢出。 - LeartS
已弃用??截至2020年6月,MacPorts中不可用。 - user5395338
1
该程序在CentOS上的ocaml-csv中(yum install ocaml-csv)。 - Russell

20

我的自由开源软件CSV流编辑器CSVfix可以完全满足您的需求。它有一个Windows的二进制安装程序,以及一个可通过makefile编译的UNIX/Linux版本。


4
顺便说一下,感谢您没有回答“为什么不自己写?”和“使用awk/perl”的问题。如果我想使用这两个选项之一,我就不会浪费时间来问这个问题了。请帮忙翻译成中文。 - Joel
3
问题在于你提问的措辞。你应该说“独立程序”,而不是“bash命令”。你的请求与bash完全无关。 - Dennis Williamson
2
csvfix 做了完全正确的事情。它是一个强大的 csv 流编辑器,可在 Windows 和 Linux 上运行,并且做得比我希望的更多! - Joel
CSVFix看起来有很多功能,但使用起来很困难,而且缺少明显的功能,比如按名称而不是索引引用列(显然它支持一些命令,但不是全部)。我会继续寻找其他工具。 - Pat Niemeyer
1
我花了一些时间才找到正确的命令,但最终我使用了order命令来完成这个任务:csvfix order -f 2,5,6 filename - user12341234
显示剩余4条评论

13

正如@Jonathan在评论中建议的那样,有一个Python模块提供了命令行工具csvfilter。它的工作方式类似于cut,但可以正确处理CSV列引用:

csvfilter -f 1,3,5 in.csv > out.csv
如果你已经安装了Python(应该已经安装了),你可以像这样简单地安装它:
pip install csvfilter

更多信息请访问https://github.com/codeinthehole/csvfilter/


10
我发现csvkit非常有用,它基于Python csv模块,对于解析复杂的CSV文件具有相当多的选项。
尽管它似乎有点慢。当从一个有5列的7GB CSV中提取一个字段时,我的速度是4MB/s(100% CPU)。
要从file.csv中提取第四列。
csvcut -c 4 file.csv

5
尝试使用crush-tools,它们非常擅长操作分隔数据。听起来正是你要找的东西。

3

我也写了一个这样的工具(仅限UNIX),叫做 csvprintf。它还可以在线将CSV转换为XML。


3

我的第一反应是在Python的csv模块周围编写一个脚本包装器(如果还没有这样的东西)。


1
有一个:'pip install cvsfilter'。 - Jonathan Hartley
1
@JonathanHartley 你是指 'csvfilter' 吗? - jmster
https://github.com/codeinthehole/csvfilter - Xiong Chiamiov

2
Perl脚本(需要Text::CSV_XS):
#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Long;
my @opt_columns;
GetOptions("column=i@" => \@opt_columns)
  or die "Failed parsing options\n";
die "Must give at least one --column\n" if int(@opt_columns) == 0;
@opt_columns = map { $_-1 } @opt_columns; # convert 1-based to 0-based

use Text::CSV_XS;
my $csv = Text::CSV_XS->new ( { binary => 1 } );

open(my $stdin, "<-") or die "Couldn't open stdin\n";
open(my $stdout, ">-") or die "Couldn't open stdout\n";
while (my $row = $csv->getline($stdin)) {
    my @nrow = @{$row}[@opt_columns];
    $csv->print($stdout, \@nrow);
    print "\n";
}

将其放入一个名为 csvcut.pl 的文件中。

只选取第三列和第四列的示例:

cat foo.csv | ./csvcut.pl --c 3 --c 4

这将仅引用需要引用的列,因此如果输入列为"Bar"(带引号),则输出将为Bar(不带引号)。


2
这看起来非常像Perl脚本解决方案,就像OP所说他不想要的那样。"我知道有很多解决方案,比如perl、awk等,但我想要一个本地的bash命令行工具,它不需要我调用其他脚本环境或编写任何额外的代码(!)。" - Jonathan Leffler

2

如果你想要一个超级轻量级的Python csv模块包装器,可以看看pluckr


“pluckr”似乎具有“csvfilter”的功能子集(例如未实现“--out-delimiter”)。 - Richard Barnett

1

ffe是另一个很棒的工具。对于大多数非琐碎任务,它需要您创建一个配置文件。好处是它非常灵活,可以处理其他工具无法处理的各种结构、逻辑和格式。

我喜欢使用csvtool来完成快速任务,并使用ffe来处理复杂任务或需要频繁重复的任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接