我的 Mac 上有大量子文件夹,每个子文件夹中都有随机数量的 csv 文件。我想将这些文件合并到每个目录中的单个文件中。
到目前为止,我知道我可以将这些文件与 合并cat * > mergedfile.csv
,但我在遍历所有文件夹时遇到了问题。到目前为止,我设法合并了各种各样的东西,但我似乎无法让它完全按照我的意愿运行。
有什么想法可以实现最佳效果吗?
for DIR in ./subfolder/*
do
cat $dir/* > merged.csv
done
答案1
使用find
,您可以递归列出符合特定条件(例如文件名)的所有文件。
for file in $(find . -type f -name "*.csv"); do cat "$file" >> /path/to/output.csv; done
分解它,find . -name "*.csv"
将从您所在的当前文件夹中找到所有 CSV 文件(.
),然后循环将遍历该列表,将所有内容附加到output.csv
文件中。
但:文件名中包含空格、通配符和换行符可能会比较棘手。更安全的解决方案是仅使用exec
find 命令。
find . -name "*.txt" -exec cat '{}' >> /path/to/output.csv ';'
这里,'{}'
将替换为文件名。有关为什么会出现这种情况以及如何规避此问题的详细问答可以在这里找到。
现在,如果您想为每个目录创建一个 CSV 文件 - 抱歉,之前没有看到 - 我可能会这样做:
for dir in $(find . -type d); do find $dir -maxdepth 1 -name "*.csv" -exec cat {} >> "$dir/out" ';'; mv "$dir/out" "$dir/merged.csv"; done
尽管下面 Franck 的解决方案可能更有效。
当然,要注意>
和之间的区别>>
。前者在写入文件之前总是会将文件截断为零长度,而后者只会将文件附加到文件中。
之所以cat *.csv > merged.csv
有效(以及为什么在循环中它不起作用)是因为 shell 之前会扩展通配符,因此基本上它会看到:
cat file1.csv file2.csv file3.csv > merged.csv
…当然不会覆盖任何内容。
答案2
进入父文件夹:
for dir in $(find . -type d); do
cd $dir
[[ $(ls *.csv|wc -l) -eq 0 ]] 2> /dev/null || { print "$dir.csv created";
cat *.csv > $dir.csv; }
cd - > /dev/null
done
答案3
假设 bash 4+(使用 检查bash --version
),你可以使用激活 globstarshopt -s globstar
并循环遍历所有目录(并且只有目录 - 尾随/
规则排除文件)使用**/
for f in **/; do cat "$f"/*.csv > "$f"/merged.csv; done
如果你真的想使用全部目录中的文件,而不仅仅是以 结尾的文件.csv
,那么
for f in **/; do cat "$f"/* > "$f"/merged.csv; done
如果您只想下降一个级别,而不是完全递归,那么请使用*/
而不是**/
。
OP 脚本中的关键错误(除了忘记 bash 区分大小写之外)是它试图写入全部将文件合并到一个.csv
文件中,并且以循环每次迭代都会覆盖上一次的方式进行。
如果你想连接全部将文件.csv
递归到单个文件中,你可以再次使用 globstar
for f in **/*.csv; do cat "$f" > merged_all.csv