我试图对一个太大而无法放入内存的文件进行排序。选项-m下的gnu排序的man表示:merge already sorted files; do not sort
。我很难理解这一点的含义,以确保排序能够实现我想要的。这篇文章(针对大型数据集的熊猫排序)建议将gnu拆分和gnu排序相结合,通过首先将文件分解成适合内存的小块,对每个文件单独排序,然后重新组合来完成这样的任务。到目前为止,我的实验似乎表明这个程序确实有效。尽管如此,我还是对手册中对合并选项的描述感到不安,该描述称它不进行排序。出于我的目的,有必要对大文件进行完全排序,而不仅仅是对本地排序的较小部分进行串联。尽管我已经在一些小例子上测试了该程序,而且它似乎有效,但该手册让我对将其应用于实际情况缺乏信心,因为我担心在无法验证gnu排序是否按我的预期运行的情况下可能会出现意外行为。
要给出MWE,请考虑我想排序的这个选项卡分隔的文件:
3 4
2 5
3 1
1 3
我尝试了以下操作:
SortDir="/Users/aireties/Desktop/Sort_Experiments"
## sort document as a whole (in practice, this would be infeasible due to document size)
sort --field-separator=$'t' -k 1,1 -k 2,2 "$SortDir/To_Be_Sorted.txt" -o "$SortDir/Sorted_as_Whole.txt" ## sort first by the first column values, then by the second
1 3
2 5
3 1
3 4
当一次对整个文件进行排序时,这是"正确"的解决方案(在我的实际用例中这是不可行的)。
如果我试图将文件分解成碎片,然后立即使用-m选项,我会得到一个错误的结果:
## Break file into pieces
MaxLines=2
mkdir "$SortDir/Pieces/"
split -l $MaxLines "$SortDir/To_Be_Sorted.txt" "$SortDir/Pieces/"
## Try merge sort on pieces without first sorting them
sort -m --field-separator=$'t' -k 1,1 -k 2,2 "$SortDir/Pieces/"* -o "$SortDir/Sorted_in_Pieces1.txt"
3 1
1 3
3 4
2 5
看起来发生的事情是,gnu排序只考虑了这两个单独的部分,并根据彼此中的第一个值对它们进行排序。因此,它将第二块放在了这个成品的第一位,但没有进行其他分类。
或者,如果我遵循这里提倡的程序(对大型数据集进行熊猫排序),即首先对片段进行排序,然后合并,我似乎确实得到了正确的结果:
for file in "$SortDir/Pieces/"* ## sorts all text files in pwd
do
sort --field-separator=$'t' -k 1,1 -k 2,2 "$file" -o "$file"
done
sort -m --field-separator=$'t' -k 1,1 -k 2,2 "$SortDir/Pieces/"* -o "$SortDir/Sorted_in_Pieces2.txt"
1 3
2 5
3 1
3 4
cmp --silent "$SortDir/Sorted_in_Pieces1.txt" "$SortDir/Sorted_as_Whole.txt" || echo "files are different"
# file are different
cmp --silent "$SortDir/Sorted_in_Pieces2.txt" "$SortDir/Sorted_as_Whole.txt" || echo "files are different"
对我来说,症结在于,如果工件文件很大,仍然需要进行大量计算,才能将它们合并为一个正确排序的文件。因此,我发现很难理解如何将如此大量的排序描述为声称其"不排序"的操作的结果
有人能告诉我为什么手册会这样措辞吗?为什么以及如何才能确信gnu排序在使用merge选项时会可靠地执行它所声称的操作?手册文本是否在某种程度上暗示了该程序无法达到预期结果的某些情况?
Gnu排序(至少是我查看源代码的版本),将对内存中的文件块进行排序,并创建一组临时文件(每个块1个临时文件)。它还在内存排序阶段使用多线程(命令行参数可以设置要使用的最大线程数)。创建完所有临时文件后,它会对临时文件进行16路合并(除非您覆盖此项),直到生成一个单独排序的文件。
这里的重点是,您不必首先将文件拆分为单独的文件,因为gnu排序将自动处理一个大文件,根据需要创建已排序的临时文件以合并为一个已排序的文件。
-m选项适用于要合并多个已排序文件的特殊情况。
-m
只是将文件合并在一起,就像mergesort的merge
操作一样。它要求两个文件按照相同的顺序进行排序。
因此,对于一个非常大的文件进行排序,您所做的确实有效:将其拆分为几个较小的文件,在本地进行排序。在这一点上,如果你只是将每个文件附加到另一个文件,你最终会得到类似0 1 2 3 ... 0 1 2 3
的东西
-m
选项确实正确地合并了它们。
例如:
a b
1 3
2 2
3 1
sort -m a b
# 1 2 3 3 2 1
sort -m a a
# 1 1 2 2 3 3
sort -m b b
# 3 2 1 3 2 1
sort -r -m b a
# 3 2 1 1 2 3
我怀疑概念问题是关于"合并"的含义。在排序算法的上下文中,"合并"具有特定的含义。看见https://en.wikipedia.org/wiki/Merge_algorithm进行讨论。一个关键点是,虽然合并操作确实将多个文件作为输入,但任何一个输入文件中的项目都必须按照正确的排序顺序进行合并,才能完成预期的操作——这与排序操作不同。从这个意义上说,"合并不排序"。
还有一种称为"合并排序"的排序算法,它将合并操作作为其组件之一。
我只是想澄清一下,因为-m / --merge
的作用对我来说并不明显:如果我们想要一个完全排序的结果,您提供的不同文件必须在之前进行排序。如果我们提供标志-m
,则sort
不排序(在man sort
中,-m
不排序,但合并)。如果我们提供未排序的文件,sort
只会尝试合并它们,按顺序读取文件,以查找提供的文件的最小数量和每个文件的当前行。
示例(具有垂直值的文件):
a | b | c | >||||||
---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 9 | <1>