将多个 .csv 文件的内容合并为单个 .csv 文件

rmb*_*rmb 23 shell text-processing csv

我想编写一个脚本,将多个 .csv 文件的内容合并到一个 .csv 文件中,即将所有其他文件的列附加到第一个文件的列中。我曾尝试使用“for”循环这样做,但无法继续。

有谁知道如何在 Linux 中做到这一点?

小智 35

实现这一目标的最简单方法是键入以下命令

cat *csv > combined.csv
Run Code Online (Sandbox Code Playgroud)

该文件将按照您提到的方式包含所有 csv 文件的内容。

  • 此答案将复制标题。使用 `head -n 1 file1.csv > combine.out && tail -n+2 -q *.csv >> combine.out` 其中 `file1.csv` 是您想要合并的任何文件。这会将所有 CSV 合并为一个像这个答案一样,但顶部只有一组标题。假设所有 CSV 共享标头。它被称为`combined.out` 以防止语句发生冲突。 (12认同)
  • 在这种特殊情况下,OP 想要的是:对于所有输入文件的每一行,附加字段行以与所有列组成一个长行,然后对所有后续行重复该过程。这很难用简单的英语明确地描述 - 我的 perl 脚本中的算法(或之后描述的更节省内存的算法)更好地解释了它并且更容易理解。 (4认同)
  • 这不会复制公共文件中的行而不是列吗? (2认同)

Chr*_*ris 20

使用paste

paste -d ',' file1.csv file2.csv ... fileN.csv
Run Code Online (Sandbox Code Playgroud)

  • 这很有效,而且它是 Linux 的一部分。这应该是正确的答案。 (5认同)

小智 10

awk '(NR == 1) || (FNR > 1)' *.csv > 1000Plus5years_companies_data.csv
Run Code Online (Sandbox Code Playgroud)

  • 如果您描述了代码正在做什么以及您对输入数据有什么期望,这将是一个有用的答案。 (2认同)
  • 我发现这也很有用,所以我会扩展...... `NR` 和 `FNR` 都表示正在处理的行数(基于 1 的索引)。FNR 是每个 *F*ile 中的当前行,而 NR 是所有文件中 *R*ecords 的当前总数。所以 `(NR == 1)` 包含第一个文件(头文件)的第一行,而 `(FNR > 1)` 跳过每个后续文件的第一行。 (2认同)

小智 9

使用csvstackcsvkit

 csvstack *.csv  > out.csv
Run Code Online (Sandbox Code Playgroud)


cas*_*cas 5

这是一个 perl 脚本,它读取命令行中指定的每个文件的每一行,并将其附加到数组 ( @csv)中的元素。当没有更多输入时,它会打印出@csv.

这些.csv文件将按照它们在命令行中列出的顺序进行追加。

警告:此脚本假定所有输入文件的行数相同。如果任何文件的行数与其他文件的行数不同,则输出可能无法使用。

#!/usr/bin/perl

use strict;

my @csv=();

foreach (@ARGV) {
  my $linenum=0;

  open(F,"<",$_) or die "couldn't open $_ for read: $!\n";

  while (<F>) {
    chomp;
    $csv[$linenum++] .= "," . $_;
  };

  close(F);
};

foreach (@csv) {
  s/^,//;   # strip leading comma from line
  print $_,"\n";
};
Run Code Online (Sandbox Code Playgroud)

给定以下输入文件:

==> 1.csv <==
1,2,3,4
1,2,3,4
1,2,3,4
1,2,3,4

==> 2.csv <==
5,6,7,8
5,6,7,8
5,6,7,8
5,6,7,8

==> 3.csv <==
9,10,11,12
9,10,11,12
9,10,11,12
9,10,11,12
Run Code Online (Sandbox Code Playgroud)

它将产生以下输出:

$ ./mergecsv.pl *.csv
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12
Run Code Online (Sandbox Code Playgroud)

好的,既然您已经阅读了这么多,是时候承认这并没有做任何没有做的事情paste -d, *.csv。那么为什么要为 perl 烦恼呢? paste很不灵活。如果您的数据完全正确,那么paste您就很好 - 它非常适合这项工作,而且速度非常快。如果没有,那对你来说完全没用。

有很多方法可以改进像这样的 perl 脚本(例如,通过计算每个文件的字段数并@csv为每个丢失的文件添加正确数量的空字段来处理不同长度的文件行。或者至少检测不同的长度并以错误退出)但如果需要更复杂的合并,这是一个合理的起点。

顺便说一句,这使用了一个非常简单的算法,并一次将所有输入文件的全部内容存储在内存 (in @csv) 中。对于现代系统上每个高达几 MB 的文件,这并非不合理。但是,如果您正在处理巨大的 .csv 文件,则更好的算法是:

  • 打开所有输入文件,同时还有输入要读取:
    • 从每个文件中读取一行
    • 追加行(按@ARGV 顺序)
    • 打印附加行