【问题标题】:Merge huge number of files into one file by reading the files in ascending order通过按升序读取文件,将大量文件合并为一个文件
【发布时间】:2019-09-15 12:16:37
【问题描述】:

我想将大量文件合并到一个文件中,这个合并文件应该根据文件名的升序进行。我尝试了以下命令,它按预期工作,但唯一的问题是合并后 output.txt 文件在一行中包含整个数据,因为所有输入文件只有一行数据而没有任何换行符。

有没有办法将每个文件数据合并到 output.txt 中作为单独的行,而不是将每个文件数据合并到一行?

我的文件列表的命名格式为9999_xyz_1.json9999_xyz_2.json9999_xyz_3.json、.....、9999_xyz_12000.json

例子:

$ cat 9999_xyz_1.json
abcdef
$ cat 9999_xyz_2.json
12345
$ cat 9999_xyz_3.json
Hello

预期输出.txt:

abcdef
12345
Hello

实际输出:

$ ls -d -1 -v  "$PWD/"9999_xyz_*.json | xargs cat
abcdef12345

编辑:

由于我的输入文件不包含任何空格或反斜杠或引号等特殊字符,因此我决定使用以下命令,该命令按预期对我有用。

find . -name '9999_xyz_*.json' -type f | sort -V | xargs awk 1 > output.txt

尝试使用包含空格的文件名,以下是 2 个不同命令的结果。

例子:

$ cat 9999_xyz_1.json
abcdef
$ cat 9999_ xyz_2.json      -- This File name contains a space
12345
$ cat 9999_xyz_3.json
Hello

预期输出.txt:

abcdef
12345
Hello

命令:

find . -name '9999_xyz_*.json' -print0 -type f | sort -V | xargs -0 awk 1 > output.txt

输出:

按预期成功完成合并,但最后出现错误。

abcdef
12345
hello

awk: cmd. line:1: fatal: cannot open file `
' for reading (No such file or directory)

命令:

这里我使用了带有-zV选项的排序来避免上述命令中出现的错误。

find . -name '9999_xyz_*.json' -print0 -type f | sort -zV | xargs -0 awk 1 > output.txt

输出:

命令成功完成,但结果不如预期。这里有空格的文件名被视为排序后的最后一个文件。期望是带空格的文件名应该在排序之后的第二个位置。

abcdef
hello
12345

【问题讨论】:

  • 在字符级别合并 json 文件,无法生成有效的 json 文件。至少您需要每个 json 文件内容成为父数据结构的子数据结构,可能是一些地图数组。这需要一个 json 解析器。
  • 您好 Léa Gris,我关心的是文件的合并,而不是文件包含的数据类型。来自多个文件的数据应该多行连接到一个文件,而不是单行。
  • 每个文件之间是否需要双换行符?如果一个换行符就足够了,那么这可能会做到shopt -s extglob; cat 9999_xyz_+([[:digit:]]).json >output.txt

标签: bash unix merge concat xargs


【解决方案1】:

我会使用for 循环来解决这个问题,并使用echo 在每个文件之间添加换行符:

for x in `ls -v -1 -d "$PWD/"9999_xyz_*.json`; do
   cat $x
   echo
done > output.txt

现在,总会有人评论说你永远不应该解析 ls 的输出,但我不确定如何以正确的顺序对文件进行排序,所以我保留了你原来的 ls 命令来枚举文件,根据您的问题工作。

编辑

您可以像@oguzismail 在他的回答中所做的那样使用awk 1 来优化这一点:

ls -d -1 -v  "$PWD/"9999_xyz_*.json | xargs awk 1 > output.txt

这个解决方案在我的机器上 4 秒内完成,在你的问题中有 12000 个文件,而 for 循环需要 13 分钟才能运行。不同之处在于for 循环启动了12000 个cat 进程,而xargs 只需要少数到awk 进程,效率要高得多。

注意:如果您想对此点赞,请务必也点赞@oguzismail 的回答,因为使用awk 1 是他的主意。但他对printfsort -V 的回答更安全,所以你可能还是想使用那个解决方案。

【讨论】:

  • 你不应该解析 ls 的输出。
  • @oguzismail 感谢您按预期执行。 :) 但说真的,你知道让*.json 以与ls -v 相同的方式订购文件的方法吗?
  • 是的,看看我的回答
【解决方案2】:

Don't parse the output of ls,请改用数组。

for fname in 9999_xyz_*.json; do
  index="${fname##*_}"
  index="${index%.json}"
  files[index]="$fname"
done && awk 1 "${files[@]}" > output.txt

另一种依赖 GNU 扩展的方法:

printf '%s\0' 9999_xyz_*.json | sort -zV | xargs -0 awk 1 > output.txt

【讨论】:

  • 这很危险,如果你在一组不同的文件上连续运行两次,它会记住最后一组未被重写的索引的文件名。
  • 它可以工作,但它真的不是很优雅,所有的文件名解析,并假设它们看起来像什么。这真的不能说服我不解析ls
  • @joanis 添加了另一种方法
  • 我更喜欢sort -zV 选项,这比创建数组要好得多。
  • 哦,男孩,POSIX 合规性总是一个很高的标准......但我同意,如果能满足它会很高兴。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
  • 2016-04-19
  • 2020-12-26
  • 1970-01-01
  • 2012-06-01
  • 2021-12-23
相关资源
最近更新 更多