合并CSV文件:追加而非合并

28

基本上,我想要合并几个CSV文件。 我正在使用以下脚本来完成:

paste -d , *.csv > final.txt

然而,这在过去对我有效,但这一次却不起作用。它将数据连接在一起而不是放置在下方。例如,两个包含以下格式记录的文件:

CreatedAt   ID
Mon Jul 07 20:43:47 +0000 2014  4.86249E+17
Mon Jul 07 19:58:29 +0000 2014  4.86238E+17
Mon Jul 07 19:42:33 +0000 2014  4.86234E+17

合并后给予

CreatedAt   ID CreatedAt    ID
Mon Jul 07 20:43:47 +0000 2014  4.86249E+17 Mon Jul 07 18:25:53 +0000 2014  4.86215E+17
Mon Jul 07 19:58:29 +0000 2014  4.86238E+17 Mon Jul 07 17:19:18 +0000 2014  4.86198E+17
Mon Jul 07 19:42:33 +0000 2014  4.86234E+17 Mon Jul 07 15:45:13 +0000 2014  4.86174E+17
                                            Mon Jul 07 15:34:13 +0000 2014  4.86176E+17

有人知道这背后的原因吗?或者有什么方法可以强制合并以下记录吗?


看起来你的一个 .csv 文件比另一个 .csv 文件有更多的行。不确定你是从哪里得到的空格。粘贴命令使用“,”来分隔条目。 - AKS
1
你的意思是执行了 cat file*.csv > final.csv 命令吗?这会把记录“一个接一个”地放在一起。祝你好运。 - shellter
“-d ,” 的目的是什么? - Cyrus
结果应该是什么样子的?你是指 join 吗? - Cyrus
@ArunSangal 哦,好的。谢谢 :) - user2233834
显示剩余2条评论
4个回答

61
假设所有的csv文件格式相同且都以相同的标题开始, 您可以编写以下脚本,将所有文件追加到一个文件中仅获取一次标题
#!/bin/bash
OutFileName="X.csv"                       # Fix the output name
i=0                                       # Reset a counter
for filename in ./*.csv; do 
 if [ "$filename"  != "$OutFileName" ] ;      # Avoid recursion 
 then 
   if [[ $i -eq 0 ]] ; then 
      head -1  "$filename" >   "$OutFileName" # Copy header if it is the first file
   fi
   tail -n +2  "$filename" >>  "$OutFileName" # Append from the 2nd line each file
   i=$(( $i + 1 ))                            # Increase the counter
 fi
done

注意:

  • head -1head -n 1 命令打印文件的第一行(头部)。
  • tail -n +2 从第二行 (+2) 开始打印文件的尾部。
  • 测试 [ ... ] 用于将输出文件从输入列表中排除。
  • 每次都会重写输出文件
  • 命令 cat a.csv b.csv > X.csv 可以简单地用来将a.csv和b.csv附加在一个文件中(但您需要复制2次标题)。

paste 命令将文件依次粘贴在一起。如果一个文件有空格作为行分隔符,则可以获得上面报告的输出。
使用 -d , 可以要求 paste 命令定义以逗号 , 分隔的字段,但是对于您报告的文件格式不是这种情况。

cat 命令连接文件并将其打印到标准输出,这意味着它会在一个文件后面写入另一个文件。

有关单个选项的语法,请参阅 man headman tail(某些版本允许使用 head -1,而其他版本则使用 head -n 1)...


我现在明白他的意思了。顺便说一下,你可以将增量放到IF语句中的“i”变量中,而不是在循环内部。 - AKS
@ArunSangal没错。我的错误,我复制了一个旧版本。如果增量在if块外部,并且输出文件是列表中的第一个,则输出文件中永远不会有标题。 - Hastur
1
这太完美了!谢谢分享。 - Yavor
注意到一个小的边角情况:如果文件名包含空格,它会出错。可以通过添加引号来解决:"$filename" - Jonik
@Jonik 没错,谢谢;已修复。在拐角处偷看是很狡猾的... 当你这样做时,你冒着发现另一个的风险:最好即使对于 $OutFileName 也加上 " ;-) - Hastur

2

另一种简单的解决方案是使用 combine_csv.sh 脚本:

#!/bin/bash
{ head -n 1 $1 && tail -q -n +2 $*; }

可以这样使用:

pattern="my*filenames*.csv"
combine_csv.sh ${pattern} > result.csv

2
不错,应该使用 && 替代 & - user239558

1

以下是我如何连接具有相同列的CSV文件:

(head -qn 1 *.csv | head -n 1; tail -qn +2 *.csv) >combined.csv

通过在任何特定文件上调用head来节省时间:

(head -n 1 first.csv; tail -n +2 *.csv) >combined.csv

不需要编写脚本或使用花哨的 awk


1
非常感谢 @wahwahwah。 我使用了您的脚本来制作 nautilus-action,但只有进行以下更改后才能正常工作:
#!/bin/bash

for last; do true; done

OutFileName=$last/RESULT_`date +"%d-%m-%Y"`.csv                       # Fix the output name

i=0                                       # Reset a counter
for filename in "$last/"*".csv"; do

 if [ "$filename" != "$OutFileName" ] ;      # Avoid recursion 
 then 
   if [[ $i -eq 0 ]] ; then 
      head -1  "$filename" > "$OutFileName" # Copy header if it is the first file
   fi
   tail -n +2  "$filename" >> "$OutFileName" # Append from the 2nd line each file
   i=$(( $i + 1 ))                        # Increase the counter
 fi
done

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