如何循环遍历目录中的所有 csv 文件、选择一系列列并合并为单个 csv?

如何循环遍历目录中的所有 csv 文件、选择一系列列并合并为单个 csv?

我在特定目录中有大量 CSV 文件。所有这些都至少有 41 列且具有匹配的标题,但宽度最多可达 200 列。我只需要获取前 40 列并将它们附加到创建一个带有标题的 CSV 中。我相对较新,正在尝试遵循这个例子如何使用 bash 将所有 csv 文件的前 200 行保留在目录中?与那个结合起来将多个 .csv 文件的内容合并到单个 .csv 文件中。我试图将其限制为一行是可能的,并且我认为我需要“cut”和“cat”命令的组合。我没有成功地尝试运行这样的东西:

$ for file in *.csv do cut -d ',' -f1-40 "$file" > "$file"; done

然后

cat *csv > combined.csv

没有任何运气。

任何意见是极大的赞赏。谢谢。

答案1

不要尝试覆盖每个文件并稍后连接,而是cut剪切所有文件并直接将结果输出到其中combined.csv

您需要确保combined.csv其本身不包含在列表中,否则最终可能会导致无限循环填满您的文件系统。

(rm -f combined.csv && set ./*.csv && cut -d, -f1-40 "$@" > combined.csv)

或者(假设 GNUxargs或兼容):

(
  rm -f combined.csv &&
    set ./*.csv &&
    printf '%s\0' "$@" |
      xargs -r0 cut -d, -f1-40 > combined.csv
)

如果文件列表csv太大,您会得到一个“参数列表太长”错误。

如果您想删除除第一个文件之外的所有文件的标头,则需要一个循环,但即使如此,您也宁愿重定向循环的输出,而不是就地编辑每个文件并稍后连接。

(
  rm -f combined.csv && set ./*.csv &&
  {
    cut -d, -f1-40 < "$1"
    shift
    for file do
      tail -n+2 < "$file" | cut -d, -f1-40
    done
  } > combined.csv
)

在任何情况下,请注意使用tailcut类似假设 csv 单元格不包含,或换行符。为了能够处理具有任意内容的 csv,您需要使用适当的 csv 操作实用程序,例如mlrorcsvtool或适当的编程语言,例如perlorpython及其 csv 模块。

答案2

如果您的系统/限制允许,请考虑使用 CSV 专用工具。我喜欢(现在维护一个分支)GoCSV

它是选择子命令的语法和功能非常相似:

for file in *.csv; do 
  gocsv select -c 1-40 $file > processed_$file
done

然后您可以将所有经过简化、处理的 CSV“堆叠”在一起:

gocsv stack processed_*.csv > combined.csv

由于 GoCSV 能够识别 CSV 格式和标头,因此您只需几行 shell 即可获得所需的结果。

也有它的-文件名选项将文件名添加到特殊的分组列中,因此您可以将任何行引用回其原始文件(并使用漂亮的打印/视图MD):

gocsv stack --filenames processed_*.csv | gocsv viewmd

| foo | File                |
|-----|---------------------|
| 1   | processed_file1.csv |
| 2   | processed_file1.csv |
| 3   | processed_file2.csv |
| 4   | processed_file2.csv |

相关内容