【问题标题】:sort across multiple files in linux在linux中对多个文件进行排序
【发布时间】:2011-12-03 08:42:48
【问题描述】:

我有多个(很多)文件;每个都很大:

file0.txt
file1.txt
file2.txt

我不想将它们合并到一个文件中,因为生成的文件将是 10+ Gigs。每个文件中的每一行都包含一个 40 字节的字符串。字符串现在排列得很好,(大约 1:10 的步长是值减少而不是增加)。

我想要订购的线路。 (如果可能的话就地?)这意味着file0.txt 末尾的一些行将移动到file1.txt 的开头,反之亦然。

我正在使用 Linux 并且对它相当陌生。我知道单个文件的sort 命令,但我想知道是否有一种方法可以跨多个文件进行排序。或者也许有一种方法可以制作由 linux 将视为单个文件的较小文件制成的伪文件。

我知道可以做什么: 我可以单独对每个文件进行排序并读入file1.txt 以找到大于file0.txt 中最大值的值(同样从file0.txt 末尾获取行),加入然后排序.. 但这很痛苦并假设来自file2.txt 的值不属于file0.txt(但在我的情况下极不可能)

编辑

要清楚,如果文件看起来像这样:

f0.txt
DDD
XXX
AAA

f1.txt
BBB
FFF
CCC

f2.txt
EEE
YYY
ZZZ

我想要这个:

f0.txt
AAA
BBB
CCC

f1.txt
DDD
EEE
FFF

f2.txt
XXX
YYY
ZZZ

【问题讨论】:

    标签: linux file sorting


    【解决方案1】:

    如果文件是单独排序的,那么您可以使用sort -m file*.txt 将它们合并在一起 - 读取每个文件的第一行,输出最小的一个,然后重复。

    【讨论】:

    • 尽管几乎所有解决方案都涉及sort -m,但我仍然完全不知道“合并”到底是什么。文档只说:“合并已经排序的文件;不要排序”,我将其解释为“按文件名排序,加入文件”。如果将它们合并为一个文件,为什么要说:“..of each file”?您的解决方案是唯一建议我重复该过程的解决方案。在什么情况下我应该重复?
    • 我只是在描述合并的作用。请参阅en.wikipedia.org/wiki/Merge_algorithm 以获得更好的描述。
    【解决方案2】:

    我相信这是你最好的选择,使用股票 linux 实用程序:

    • 单独对每个文件进行排序,例如for f in file*.txt; do sort $f > sorted_$f.txt; done

    • 使用sort -m sorted_file*.txt | split -d -l <lines> - <prefix> 对所有内容进行排序,其中<lines> 是每个文件的行数,<prefix> 是文件名前缀。 (-d 告诉 split 使用数字后缀)。

    -m 排序选项让它知道输入文件已经排序,所以它可以很聪明。

    【讨论】:

    • 我收到错误“无法打开'sorted_file'进行阅读”,其中sorted_file 应该是我的新文件前缀(我假设)。这些空文件需要提前存在吗?此外,文档说 -m arg 不会排序。我假设它只对文件名进行排序并按文件名排序的顺序合并它们。
    • 呃,就像我说的,您需要单独对每个文件进行排序。我澄清了第一步。而且您误读了文档:“合并已排序的文件;不排序”意味着它不对输入文件进行排序,而不是根本不排序。将其视为合并排序的一个步骤。您可以在测试用例上进行尝试以向自己证明。
    • 呃,我试了一个测试用例。就像我说的,我收到错误:“无法打开 'output_file' 进行阅读”,其中 'output_file' 是您的 <prefix>。 (如果我混淆了您的文件名,我深表歉意。)我认为您有错字。也许您需要像@JBert 的解决方案一样的破折号?我不知道。就像我在 Q 中所说的那样,我是一个完整的 Linux “菜鸟”。如果合并对最终文件进行排序,那么我为什么要对输入进行预排序?也许合并正在做一些事情,比如以有序的方式交错行?
    • @Paul:你的最后一句话是正确的。最终输出是排序的,假设输入是单独排序的。当我说尝试一个测试用例来验证这一点时,我的意思是两个文件,一个包含第 1、3、5 行,另一个包含第 2、4、6 行,只是为了看到它交错。而且我的解决方案在功能上与 JBert 和 sarnold 的解决方案相同;我在他们之前发布了一些帖子,他们明确了第一步,使用了不同的文件名等。我会更详细地编辑。
    • 谢谢,不过,您的“排序所有内容”命令应为:sort -m sorted_file*.txt | split -d -l <lines> - <prefix> 表示从标准输入读取。 computerhope.com/unix/usplit.htm(至少在我的机器上)
    【解决方案3】:

    这并不完全符合您的要求,但使用 --merge 选项,sort(1) 实用程序可以提供一点帮助。分别对每个文件进行排序,然后对生成的文件堆进行排序:

    for f in file*.txt ; do sort -o $f < $f ; done
    sort --merge file*.txt | split -l 100000 - sorted_file
    

    (每个输出文件有 100,000 行。也许这仍然太小了。)

    【讨论】:

    • 我收到一个错误:“无法打开 `sorted_file' 进行阅读”。我认为这个命令会创建这样一个文件?我不清楚 -m arg。文档说:“不要排序”。你确定它会跨文件边界对行进行排序吗?
    • @Paul,很抱歉这个错误!我忘记了 split(1)- 参数告诉它从 stdin 读取输入。我没有注意到split(1) 联机帮助页中的PREFIX 命令行参数只有在明确给出输入文件时才有可能。 -m 选项到sort(1) 将从给定的预排序 输入文件中选择正确的行来输出。这是merge sort 中的final 步骤,它正确地整理了来自已排序的输入文件的输出。
    • 啊! - 它整理 - 这正是正在发生的事情,并且比“合并”更具描述性。谢谢。
    【解决方案4】:

    我不知道执行就地排序的命令,但我认为更快的“合并排序”是可能的:

    for file in *.txt; do
        sort -o $file $file
    done
    sort -m *.txt | split -d -l 1000000 - output
    
    • for 循环中的sort 确保输入文件的内容已排序。如果不想覆盖原来的,只需更改-o 参数后面的值即可。 (如果您希望文件已经排序,您可以将排序语句更改为“仅检查”:sort -c $file || exit 1
    • 第二个sort 有效地合并输入文件,同时保持输出排序。
    • 这通过管道传送到split 命令,然后该命令将写入带后缀的输出文件。注意- 字符;这告诉 split 从标准输入(即管道)而不是文件中读取。

    此外,这里是合并排序如何工作的简短摘要:

    1. sort 从每个文件中读取一行。
    2. 它对这些行进行排序并选择应该首先出现的行。此行被发送到输出,并从包含此行的文件中读取一个新行。
    3. 重复第 2 步,直到任何文件中都没有更多行。
    4. 此时,输出应该是一个完美排序的文件。
    5. 利润!

    【讨论】:

    • 我从来不知道sort(1) 足够聪明,可以在打开输出之前打开输入。谢谢!
    • 其实它使用的是临时文件。
    • 这是有道理的。不过,最终的结果还是不错的。 :)
    • 出于好奇,您能解释一下为什么output 文件名前缀之前的- 是必要的(或它的用途)吗?除您之外的所有其他解决方案都给我错误:cannot open 'output' for reading(搜索“Linux 命令行水平管道”没有返回任何内容!):D
    • 似乎其他人没有阅读split man page。我已经编辑了我的答案。
    【解决方案5】:

    mmap() 3 个文件,因为所有行都是 40 字节长,您可以轻松地对它们进行排序 (SIP :-)。不要忘记最后的 msync。

    【讨论】:

      猜你喜欢
      • 2018-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 2013-10-06
      相关资源
      最近更新 更多