gnu-sort - 当它说合并选项 "not sort" 时,手册是什么意思

gnu-sort - what does manual mean when it says merge option does "not sort"

我正在尝试对一个太大而无法放入内存的文件进行排序。选项 -m 下的 gnu sort 说明:merge already sorted files; do not sort。我正在努力理解这一点的含义,以确保这种排序能够完成我想要的。 post (Sorting in pandas for large datasets) 建议结合使用 gnu split 和 gnu sort 来完成这样的任务,方法是首先将文件分成适合内存的较小部分,然后分别对每个部分进行排序,然后重新组合。到目前为止,我的实验似乎表明这个过程确实有效。尽管如此,我对手册中的合并选项的描述说它不排序感到困扰。出于我的目的,有必要对大文件进行完全排序,而不仅仅是在本地排序的较小文件的串联。虽然我已经在小例子上测试了这个程序并且它似乎有效,但是手册让我对将它应用到我的实际情况缺乏信心,因为我担心在验证 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

这是一次对整个文件进行排序时的"correct"解决方案(这在我的实际用例中是不可行的)。

如果我尝试将文件分成多个部分然后立即使用 -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 sort 刚刚考虑了两个单独的部分并根据彼此的第一个值对它们进行了排序。因此,它在这个成品中将第二块放在了第一位,但没有进行其他排序。

或者,如果我按照这里提倡的程序(Sorting in pandas for large datasets),即先排序然后合并,我似乎得到了正确的结果:

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"

对我来说,症结在于,如果片段文件很大,仍然需要进行大量计算才能将它们合并为一个正确排序的文件。因此,我发现很难理解如何将如此大量的排序描述为声称它确实如此的操作的结果 "not sort."

谁能告诉我为什么手册会这样写?为什么以及如何能够确信 gnu sort 在使用合并选项时能够可靠地执行它所声称的操作?手册文本是否以某种方式暗示了此过程无法达到预期结果的某些情况?

-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

Gnu 排序(至少是我查看源代码的版本),将对内存中的文件块进行排序并创建一组临时文件(每个块一个临时文件)。它还在内存排序阶段使用多线程(命令行参数可以设置要使用的最大线程数)。创建所有临时文件后,它会对临时文件进行 16 次合并(除非您覆盖此操作),直到生成单个排序文件。

这里的重点是您不必先将文件拆分成单独的文件,因为 gnu sort 会自动处理大文件,根据需要创建排序的临时文件以合并到单个排序的文件中。

-m 选项用于合并多个已排序文件的特殊情况。

我怀疑概念上的问题与 "merge" 的含义有关。在排序算法的上下文中,"merge" 具有特定的含义。请参阅 https://en.wikipedia.org/wiki/Merge_algorithm 进行讨论。一个关键点是,虽然合并操作确实将多个文件作为输入,但任何单个输入文件中的项目都必须按正确排序的顺序排列,以便合并执行预期的操作——这与排序不同手术。在这个意义上"merge does not sort".

还有一种名为 "merge sort" 的排序算法,它使用合并操作作为其组成部分之一。

澄清一下,因为对我来说 -m / --merge 的作用并不是很明显:如果我们想要一个完全排序的结果。如果我们提供标志 -m sort 不排序(在 man sort 中说 -m 不排序 ,但是 合并)。如果我们提供未排序的文件,sort 将尝试合并它们,依次读取文件以查找提供的文件的最小数量和每个文件的当前行。

示例(具有垂直值的文件):

a b c d e
1 2 1 8 3
3 4 5 3 2
5 6 9 5 1

文件a、b、c已排序; d 和 e 未排序。所以:

sort -m a b: 1 2 3 4 5 6 
sort -m b c: 1 2 4 5 6 9
sort -m b d: 2 4 6 8 3 5
sort -m c d: 1 5 8 3 5 9
sort -m a e: 1 3 3 2 1 5

文件c和d的情况:

sort -m logic:

  c d
  ---
->1 8<-
  5 3
  9 5

min(1, 8)? -> 1 and point to the next row in the file c
Result: 1

  c d
  ---
  1 8<-
->5 3
  9 5

min(5, 8)? -> 5 and point to the next row in the file c
Result: 1 5

  c d
  ---
  1 8<-
  5 3
->9 5

min(9, 8)? -> 8 and point to the next row in the file d
Result: 1 5 8

  c d
  ---
  1 8
  5 3<-
->9 5

min(9, 3)? -> 3 and point to the next row in the file d
Result: 1 5 8 3

  c d
  ---
  1 8
  5 3
->9 5<-

min(9, 5)? -> 5 and point to the next row in the file d
Result: 1 5 8 3 5

  c d
  ---
  1 8
  5 3
->9 5

min(9, inf)? -> 9 and point to the next row in the file c
Result: 1 5 8 3 5 9

  c d
  ---
  1 8
  5 3
  9 5

min(inf, inf)? -> we have finished
Result: 1 5 8 3 5 9

注意:cat a b | sort -m 将不起作用,因为 sort 确实需要其他人解释的文件描述符。