「sort(1)」-

排序文本文件中的行

命令语法格式

sort [OPTION]… [FILE]…

sort [OPTION]… –files0-from=F

命令描述

命令sort对给定文件FILE中的所有行进行排序、合并、比较。

如果文件FILE没有给出,或者为“-”,,则从标准输入中读取数据进行排序。默认情况下,sort将结果写入标准输出中。

许多选项影响sort比较行的方式;如果输出不是预期的结果,可以尝试使用–debug选项查看发生的情况。一对行的比较过程如下:sort按命令行中指定的顺序比较每对字段(参阅–key),根据相关的排序选项,直到找到二者的差异或没有剩余字段。如果未指定任何比较字段,则sort将使用整行的默认键。最后,如果所有字段通过比较是相等的,则sort进行比较整行,就好像没有指定除–reverse(-r)之外的任何排序选项。–stable(-s)选项可以禁用这个最后比较,以便所有字段比较在相等的情况下保留其原始相对顺序。–unique(-u)选项也会禁用最后的比较。

除非另有说明,否则所有比较都使用LC_COLLATE语言环境指定的字符整理顺序。为了进行比较,结尾的换行符号不是该行的一部分。如果输入文件FILE的最后一个字节不是换行符,则GNU的sort会静默提供一个。GNU的sort对输入行长度或对行内允许的字节都没有限制。

命令sort的三种模式

命令sort有三种操作模式:排序(默认)、合并、检查排序。

以下选项可用于指定操作模式:

-c, –check, –check=diagnose-first
检查给定文件是否已排序:如果未排序,则打印诊断信息,该诊断信息中描述了第一个无序的行,并以状态1退出。否则,成功状态0退出。

使用该选项时,最多只能给出一个输入文件。

-C, –check=quiet, –check=silent
如果给定文件已经排序,则成功退出,否则状态1退出。该选项类似于-c选项,但是该选项不打印诊断信息。

使用该选项时,最多只能给出一个输入文件。

-m, –merge
将给定的多个文件FILE作为一个组来进行排序。每个输入文件必须始终先进行单独排序。它始终用于排序,而不是合并;提供合并是因为它更快,在它工作的情况下。

默认行为
如果没有指定上述的选项,则执行默认行排序功能。

命令支持的选项及含义

影响输出顺序

它们可以全局指定,也可以作为特定排序字段的一部分。如果未指定排序字段,则全局选项用于整行比较;否则全局选项将由未指定其自身的任何特殊选项的排序字段继承。在sort的前POSIX版本中,全局选项仅影响后面的排序字段,因此可移植Shell脚本应首先指定全局选项。

-b, –ignore-leading-blanks
在每行中查找排序列时,忽略前导空白。默认情况下,“空白”是空格或制表符,但LC_CTYPE语言环境可以更改此值。

注意,你的本地语言设置的整理规则可能会忽略空白,但如果没有此选项,它们对于使用-k选项在排序列中指定的字符位置会很重要。

-f, –ignore-case
在比较时,将小写转化为大写后再进行比较,即忽略字母的大小写。默认也是忽略大小写的。例如’b’和’B’在排序上是相等。环境变量LC_CTYPE语言环境用于确定字符类型。

如果要进行大小敏感的排序,可以使用LC_COLLATE=C sort...或者LC_CTYPE=upper sort...的形式执行sort命令。

当与选项–unique一起使用时,这些小写的等效行会被丢弃。目前,还没有办法丢弃大写的等效行。任何–reverse选项只会影响最终结果,即在扔掉之后的一个排序。

-i, –ignore-nonprinting
忽略非打印字符。LC_CTYPE语言环境决定了字符类型。如果还给出了更强的–dictionary-order(-d)选项,则此选项无效。

-d, –dictionary-order
按“电话目录顺序”进行排序:排序时忽略除字母、数字、空白之外的所有字符。字母和数字是ASCII字符。默认情况下,而空白则是空格或制表符,但LC_CTYPE语言环境可以更改此值。

-g, –general-numeric-sort, –sort=general-numeric
按数字排序,将每行的前缀转换为长双精度浮点数。请参阅「浮点」。不报告溢出、下溢、转换错误。使用以下整理顺序:

  • 不以数字开头的行(都被视为相等)。
  • NaN(IEEE浮点运算中的“非数字”值)被视为一致,但依赖于机器的顺序。
  • 减号视为无限(Minus infinity)。
  • 有限数字则按升序排序(-0和+0相等)。
  • 加号视为无限(Plus infinity)。

只有在没有其他选择的情况下才使用此选项; 它比–numeric-sort(-n)慢得多,并且在转换为浮点数时可能会丢失信息。

-h, –human-numeric-sort, –sort=human-numeric
数字排序。首先是数字符号(负数、零、正数),然后按SI后缀(空、k、K、M、G、T…,按此顺序;参见「块大小」),最后是数值排序。例如,’1023M’排在’1G’之前,因为作为SI后缀,’M’(兆)在’G’(千兆)之前。

此选项将一致地缩放到最接近后缀的值再进行排序,无论后缀是表示1000,还是1024的倍数,因此它可以对使用–human-readable或–si选项单个调用的df、du、ls等命令的任何输出进行排序。

注意,输入中数字的语法与–numeric-sort选项的语法相同;SI后缀必须紧跟数字后跟。另请注意numfmt(1)命令,该命令可用于在sort排序后,再次将数字重新格式化为易读格式,因此通常使用sort操作更准确的数字。

-n, –numeric-sort, –sort=numeric
按数字排序。数字从每一行开始,由可选空格,可选的“-”符号,零个或多个数字组成,并可能由千位分隔符分隔,及后跟可选的小数点字符及其后零个或多个数字。空数值被视为“0”。

LC_NUMERIC语言环境指定小数点字符和千位分隔符。默认情况下,空白是空格或制表符,但LC_CTYPE语言环境可以更改此值。

进行精准的比较;没有舍入错误。

无法识别前导“+”和指数表示法。要以数字方式来比较此类有前导加号的字符串,请使用–general-numeric-sort(-g)选项。

-M, –month-sort, –sort=month
由任意数量的空白并且后跟月份名称缩写组成的初始字符串,会被转化为大写,并按‘JAN’ < ‘FEB’ < … < ‘DEC’的顺序进行比较。无效的名称将与最低的有效名称进行比较。

LC_TIME区域设置类别确定月份拼写。默认情况下,空白是空格或制表符,但LC_CTYPE语言环境可以更改此值。

-V, –version-sort
按版本名称和编号排序。该选项的行为类似于标准的排序行为,除了每个十进制数字序列在数字上会被视为索引/版本号。参阅「版本排序的详细信息」。

-r, –reverse
反转比较结果。让具有更大键值的行在输出中最开始,而不是默认出现在最后面。

-R, –random-sort, –sort=random
计算输入的排序字段的哈希值,然后对哈希值进行排序。随机选择哈希函数,确保它没有冲突,以便不同的排序字段具有不同的哈希值。这就像输入的随机排列(参见shuf(1)调用),除了具有相同值的字段会排序在一起。

如果指定了多个随机排序字段,则对所有字段使用相同的随机散列函数。要为不同的字段使用不同的随机散列函数,你可以多次调用排序命令。

哈希函数的选择受–random-source选项的影响。

下面是其他选项

–compress-program=prog
使用程序prog压缩任何临时文件。如果没有参数,prog必须将标准输入压缩为标准输出,并且当给定-d选项时,它必须将标准输入解压到标准输出。空白和反斜杠字符不应出现在prog中; 它们留作将来使用。

如果prog以非零状态退出,则命令sort终止并返回错误。

–files0-from=file
禁止处理在命令行上指定的文件,而是处理由文件file的内容所指定的文件;其中,每个文件名以零字节(NUL)终止。

当我们要将多个文件一起排序,而文件名列表太长以至于可能超出命令行长度限制时,这很有用。在这种情况下,通过xargs(1)运行sort命令是不可取的,因为xargs(1)将文件列表拆分为多个部分,并为每个子列表进行排序,而不是整个列表,然后打印排序输出也是单个文件的排序。生成由NUL字符分隔的文件名列表的一种方法是使用GNU的find(1)命令的-print0选项。如果file为’-‘,则从标准输入读取由NUL分隔的文件名列表。

-k pos1[,pos2], –key=pos1[,pos2]
指定一个排序字段,该字段由第pos1列与第pos2列之间的列组成。如果省略了pos2,则到行尾。

在最简单的形式中,pos指定了一个字段编号(从1开始),字段之间由空白字符分隔。默认情况下,这些空白又会包含在每个字段开头中,并参与比较。要调整空白字符的处理,请参阅-b和-t选项。

一般地,每个pos的形式为’f[.c][opts]’:其中f是要使用的字段的编号;c是从字段的开头起的第一个用于排序的字符的编号。字段和字符位置从1开始编号;pos2中的字符位置(c)为零,则表示该字段的最后一个字符。如果从pos1中省略‘.c’,则默认为1(字段的开头);如果从pos2中省略‘.c’,则默认为0(字段的结尾)。opts是排序选项,允许根据不同的规则对各个字段进行排序;详见下文。排序字段可以跨越多个字段。

比如,要对第二个字段进行排序,请使用–key=2,2。有关排序字段和更多示例,请参见下文。另请参阅–debug选项,以帮助确定参与排序的行的部分。

–debug
突出显示每一行中用于排序的部分。同时向标准错误中输出有关可疑用法的警告。

–batch-size=nmerge
一次合并最多nmerge输入。

当sort必须合并多于nmerge个输入时,它将它们以nmerge为组进行合并,将结果保存在临时文件中,然后将其用作后续合并中的输入。

较大的nmerge值可以提高合并性能,并降低临时存储利用率,但会增加内存使用和I/O。相反,较小的nmerge值可能会以消耗临时存储和性能为代价来降低内存使用和I/O.

参数nmerge的值必须至少为2。当前默认值为16,但这取决于实现,将来可能会更改。

参数nmerge的值可能受打开文件描述符的资源限制的限制。命令’ulimit -n’或’getconf OPEN_MAX’会显示系统的限制;如果你的程序已经打开某些文件,或者操作系统对打开文件的数量有其他限制,则可以进一步修改这些限制。如果nmerge的值超出资源限制,则静默排序使用较小的值。

-o output-file, –output=output-file
将输出写入output-file,而不是标准输出。通常,sort在打开output-file文件之前读取所有输入,因此你可以使用sort -o F Fcat F | sort -o F等命令对文件进行排序,并写入文件。但是,输出到其他未使用的文件通常更安全,因为如果系统崩溃或排序在文件正在排序时遇到I/O或其他严重错误,数据可能会丢失。另外,使用–merge(-m)选项的sort命令会在读取所有输入之前打开输出文件,因此像cat F | sort -m -o F – G这样的命令并不安全,因为在完成cat读取之前,sort可能会开始写入文件F。

在较新的系统上,如果设置了POSIXLY_CORRECT,则选项-o不能出现在输入文件之后,形如sort F -o F。因此,可移植脚本应在任何输入文件之前指定-o output-file选项。

–random-source=file
使用文件file作为随机数据的来源,用于确定与-R选项一起使用的随机散列函数。请参阅「随机源」一文。

-s, –stable
通过禁用sort最后重排序比较来使排序稳定。如果未指定除了–reverse(-r)以外的字段或未指定全局排序选项,则此选项无效。

-S size, –buffer-size=size
使用给定大小的主内存排序缓冲区。默认情况下,size以1024字节为单位。附加’%’会导致size被解释为物理内存的百分比。附加’K’将大小乘以1024(默认值);’M’乘以1,048,576;’G’乘以1,073,741,824;依此类推还有’T’,’P’,’E’,’Z’,’Y’;附加’b’会导致大小被解释为字节数。

此选项可以通过使其以比默认值更大或更小的排序缓冲区开始进行排序,以此来提高排序性能。但是,此选项仅影响初始缓冲区大小。如果sort遇到大于size的输入行,则缓冲区将超出size所指定的大小。

-t separator, –field-separator=separator
在每行中查找排序字段时,使用字符separator作为字段分隔符。默认情况下,字段由非空白字符和空白字符之间的空字符串分隔。默认情况下,空白是空格或制表符,但LC_CTYPE语言环境可以更改此值。

也就是说,给定输入行’ foo bar’,sort将其分为字段’ foo’和’ bar’。字段分隔符不被视为前一个字段或字段后面的一部分,因此,对’ foo bar’使用sort -t ” “会有三个字段:空字段,’foo’,’bar’。但是,扩展到行尾的字段(-k 2)或由范围组成的字段(-k 2,3)将保留范围端点之间存在的字段分隔符。

要将NUL指定为字段分隔符,使用字符串’\0’,例如sort -t ‘\0’

-T tempdir, –temporary-directory=tempdir
使用目录tempdir来存储临时文件,覆盖环境变量TMPDIR的值。如果多次给出此选项,则临时文件将存储在给定的所有目录中。如果你有一个受I/O限制的大型排序或合并操作,则通常可以使用此选项来指定不同磁盘和控制器上的目录,以此提高性能。

–parallel=n
将并行运行的sort数设置为n。默认情况下,将n设置为可用处理器的数量,但限制为8,因为之后性能收益会减少。

另请注意,使用n个线程会将内存使用量增加log n。另请参阅nproc(1)调用。

-u, –unique
使用该选项后,多个行的比较结果相等时,只输出这些行中的第一行。如果同时指定了–check(-c, -C)选项,则检查中没有一对连续行比较是相等的,即将相等的行视为无序行。

此选项还会禁用默认的最后重排比较。

命令sort -usort | uniq是等价的。但是,这个等价不会扩展到任意sort选项。例如,命令sort -n -u在检查唯一性时仅检查初始数字字符串的值,而命令sort -n | uniq检查整行。请参见uniq(1)调用。

-z, –zero-terminated
使用零字节(NUL)而不是换行符(ASCII LF)分隔项。即,将输入视为是由NUL字符分隔的项,并使用NUL字符分隔输出项。

此选项可与’perl -0’、’find -print0’、’xargs -0’结合使用,它们可以相同地处理任意文件名(甚至包含空格或其他特殊字符的文件名)。

注意事项

如果你使用非POSIX语言环境(例如,通过将LC_ALL设置为’en_US’),则sort可能会生成与你习惯的排序不同的输出。在这种情况下,将LC_ALL环境变量设置为“C”。请注意,仅设置LC_COLLATE有两个问题。首先,如果还设置了LC_ALL,则无效。其次,如果将LC_CTYPE(或LANG,如果未设置LC_CTYPE)设置为不兼容的值,则它具有未定义的行为。例如,如果LC_CTYPE是ja_JP.PCK但LC_COLLATE是en_US.UTF-8,则会得到未定义的行为。

兼容性

在sort的实现历史中,不同的实现(BSD和System V)对某些选项的解释不同,特别是-b、-f、-n。GNU的sort遵循POSIX行为,通常类似于System V行为(但不总是!)。根据POSIX,选项-n不再隐含-b选项。为了保持一致性,选项-M以相同的方式进行了更改。在不明显的情况下,这可能会影响字段规范中字符位置的含义。唯一的解决方法是显式指定-b选项。

使用-k指定的排序字段中的位置可以附加任何选项字母“MbdfghinRrV”,在这种情况下,该特定字段不会继承全局排序选项。选项-b可以独立地附加到字段规范的开始和结束位置中的任一个或两个,并且如果它是从全局选项继承的,则它将附加到两者。如果输入行可以包含前导或相邻空白并且未使用-t,则-k通常与-b或隐式忽略前导空格(’Mghn’)的选项组合,否则字段中前导空格的变化数量可能导致令人困惑的结果。

如果排序字段说明符中的起始位置设置在了行尾或结束字段之后,则该字段为空。如果指定了-b选项,则字段规范的“.c”部分表示从字段的第一个非空字符开始计算。

在不符合POSIX 1003.1-2001的系统上,sort支持传统的origin-zero语法’+pos1 [-pos2]’来指定排序键。传统的命令sort +ax -b.y,如果y为’0’或不存在,则相当于sort -k a+1.x+1,b,否则它相当于sort -k a+1.X+1,b+1.y

可以使用_POSIX2_VERSION环境变量控制此传统行为(请参阅「标准一致性」);如果未设置POSIXLY_CORRECT,也可以启用它,通过使用存在’-pos2’的传统语法。

打算在标准主机上使用的脚本应该避免使用传统语法,而应该使用-k选项。例如,避免sort +2,因为它可能被解释为sort ./+2sort -k 3。如果你的脚本还必须在仅支持传统语法的主机上运行,​​则它可以使用类似if sort -k 1 </dev/null >/dev/null 2>&1; then…的测试命令来决定使用哪种语法。

相关环境变量

TMPDIR
如果设置了环境变量TMPDIR,则sort将其值用作临时文件的目录。而不使用默认的/tmp目录。–temporary-directory(-T)选项会覆盖该环境变量。

退出状态

0 执行成功,没有错误产生。

1 使用了-c或者-C选项,但是文件没有排序。

2 其他错误。

使用示例

以下是一些示例,用于说明各种选项组合。

按数字降序顺序进行排序:

# sort -n -r

使用10M的缓冲区大小,同时运行不超过4个排序:

# sort –parallel=4 -S 10M

按字母顺序排序,省略第一个和第二个字段以及第三个字段开头的空白。下面的命令使用单个排序字段,该排序字段由第3个字段中的第一个非空白字符开头处的字符组成,并到每行尾:

# sort -k 3b

使用’:’作为字段分隔符,在第2个字段上按数字排序,并通过在第5个字段的第3个和第4个字符上按字母顺序排序来解决关联:

# sort -t : -k 2,2n -k 5.3,5.4

请注意,如果你使用了-k 2n(而不是-k 2,2n),则 sort会使用从第2个字段开始的所有字符直到该行的末尾,并将其作为主数字键。对于大多数应用程序,将跨越多个字段的键作为数字处理将不会达到预期效果。

另请注意,’n’修饰符已应用于第一个键的字段结束说明符。它等同于指定-k 2n,2-k 2n,2n。除了’b’之外的所有修饰符都适用于相关字段,无论修饰符字符是否附加到键说明符的字段开始或字段结尾部分。

对一组日志文件进行排序,主要是通过IPv4地址,其次是按时间戳。如果两行的主键和辅助键相同,则以与输入相同的顺序进行输出。日志文件包含如下所示的行:

4.150.156.3 - - [01/Apr/2004:06:31:51 +0000] message 1
211.24.3.231 - - [24/Apr/2004:20:17:39 +0000] message 2

上面的字段由一个空格分隔。按字典顺序对IPv4地址进行排序,例如,212.61.52.2排在212.129.233.201之前,因为61小于129:

# sort -s -t ‘ ‘ -k 4.9n -k 4.5M -k 4.2n -k 4.14,4.21 file*.log | sort -s -t ‘.’ -k 1,1n -k 2,2n -k 3,3n -k 4,4n

使用单次sort调用无法完成此示例,因为IPv4地址组件由“.”分隔,而日期位于空格之后。因此,它分为两个sort调用:第一次调用按时间戳排序;第二次调用按IPv4地址排序。时间戳按年份,然后是月份,然后是日期,最后按小时、分钟、秒字段排序,使用-k隔离每个字段。除了时分秒之外,不需要指定每个排序字段的结束位置,因为’n’和’M’修饰符基于不能跨越字段边界的前导前缀进行排序。IPv4地址按字典顺序排序。第二个调用使用’-s’,以便主键中的连接被辅助键断开;第一个中使用’-s’,以便两种排序的组合是稳定的。

以不区分大小写的排序顺序生成标记文件:

# find src -type f -print0 | sort -z -f | xargs -0 etags –append

在这种情况下使用-print0、-z、-0意味着包含空格或其他特殊字符的文件名不会被排序操作分解。

使用常见的DSU(Decorate Sort Undecorate)成语来根据行的长度对行进行排序:

# awk ‘{print length, $0}’ /etc/passwd | sort -n | cut -f2- -d’ ‘

通常,此技术可用于对sort命令不支持、或直接排序会低效。

随机播放目录列表,但保留每个目录中的文件顺序。例如,可以使用它来生成音乐播放列表,其中混淆了专辑但是按顺序播放每个专辑的歌曲。

# ls */* | sort -t / -k 1,1R -k 2,2

上述命令将目录随机排序,然后文件名顺序排序。

相关手册

shuf(1), uniq(1)

查看在线手册:http://www.gnu.org/software/coreutils/sort

查看info手册:info ‘(coreutils) sort invocation’

参考文献

  • man 1 sort, version GNU coreutils 8.28
  • 关于本地语言的内容可以参考OpenGroup中「7.Locale」部分的说明

更新日志

  • 07/19/2017 创建文章
  • 07/09/2018 按照GNU的手册修改文件内容