Linux基础:文本处理工具之sort

大纲

1、sort是什么?

2、sort语法格式

3、sort工作原理

4、sort实战演练


1、sort是什么?

  In Unix-like operating systems, sort is a standard command line program that prints the lines of its input or concatenation of all files listed in its argument list in sorted order. Sorting is done based on one or more sort keys extracted from each line of input. By default, the entire input is taken as sort key. Blank space is the default field separator.


2、sort语法格式

我们可以通过 sort --help来查看命令的帮助

[root@localhost ~]# sort --help
Usage: sort [OPTION]... [FILE]...
Write sorted concatenation of all FILE(s) to standard output.

Mandatory arguments to long options are mandatory for short options too.
Ordering options:

  -b, --ignore-leading-blanks  ignore leading blanks
  -d, --dictionary-order     consider only blanks and alphanumeric characters
  -f, --ignore-case         fold lower case to upper case characters
  -g, --general-numeric-sort    compare according to general numerical value
  -i, --ignore-nonprinting    consider only printable characters
  -M, --month-sort        compare (unknown) < `JAN' < ... < `DEC'
  -n, --numeric-sort       compare according to string numerical value
  -r, --reverse          reverse the result of comparisons

Other options:

  -c, --check           check whether input is sorted; do not sort
  -k, --key=POS1[,POS2]   start a key at POS1, end it at POS2 (origin 1)
  -m, --merge           merge already sorted files; do not sort
  -o, --output=FILE        write result to FILE instead of standard output
  -s, --stable        stabilize sort by disabling last-resort comparison
  -S, --buffer-size=SIZE    use SIZE for main memory buffer
  -t, --field-separator=SEP  use SEP instead of non-blank to blank transition
  -T, --temporary-directory=DIR  use DIR for temporaries, not $TMPDIR or /tmp;
                    multiple options specify multiple directories
  -u, --unique              with -c, check for strict ordering;
                              without -c, output only the first of an equal run
  -z, --zero-terminated     end lines with 0 byte, not newline
      --help     display this help and exit
      --version  output version information and exit

POS is F[.C][OPTS], where F is the field number and C the character position
in the field.  OPTS is one or more single-letter ordering options, which
override global ordering options for that key.  If no key is given, use the
entire line as the key.

SIZE may be followed by the following multiplicative suffixes:
% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y.

With no FILE, or when FILE is -, read standard input.

这里列出许多常用的选项, 下面的实战演练部分将演示这些选项的作用。


3、sort工作原理(排序文本)


工作原理描述

  sort 命令对 FILE 参数指定的文件中的行排序,并将结果写到标准输出。如果 FILE 参数指定多个文件,那么 sort 命令将这些文件连接起来,并当作一个文件进行排序。-(减号)代替文件名指定标准输入。如果未指定任何文件名,那么该命令对标准输入排序。可以使用 -o 标志指定输出文件。如果未指定任何标志,sort 命令基于当前语言环境的整理顺序对输入文件的所有行排序。

 就像awk,cut,join一样:sort将输入看作具有多条记录的数据流,而记录是由可变宽度的字段组成,记录是以换行字符作为定界符,字段的定界符则是空白字符或是用户指定的单个字符。

  1. 以最简单的情况来说,如果未提供命令行选项时,整个记录都会根据当前locale所定义的次序排序。从首字符一直向后,依次按locale所定义的次序排序。所以,在排序之前我们最好定义locale,如下:LANG=C。

  2. 如果要进一步控制排序操作,可以用 �Ck 选项指定排序字段,并且用�Ct选项来选择字段定界符。如未指定�Ct,则表示字段以空白分隔且记录内开头与结尾的空白都将忽略;如果指定-t选项,则被指定的字符会分隔字段,且空白是有意义的。

  • 如果仅指定一个字段编号,则排序键值会自该字段的起始处开始,一直继续到记录的结尾(而非字段的结尾)。-k2 表示从第2个字段开始,到整个记录结尾。-k POS1[,POS2] start a key at POS1 (origin 1), end it at POS2 (default end of line)。如果仅指定第2个字段呢?正解:-k2,2

  • 如果给的是一对用逗号隔开的字段数字,则排序键值将由第一个字段值的起始处开始,结束于第二个字段值的结尾。

  • 使用点号字符位置,则比较的开始(一对数字的第一个)或结束(一对数字的第二个)在该字符位置处:-k2.4,5.6指的是从第2个字段的第4个字符开始比较,一直比到第5个字段的第6个字符。

  • 当出现多个 -k选项,会先从第一个键值字段开始排序,找出匹配该键值的记录后,再进行第二个键值字段的排序,以此类推。


排序关键字

  排序关键字是输入行的一部分,由字段号和列号指定。字段是输入行的组成部分,由字段分隔符分隔。缺省字段分隔符是由一个或多个连续空白字符组成的序列。然而,这些空白字符被看作以下用于排序的字段的一部分。您可以指定 -b 选项来忽略这些开头的空白字符。使用 -t 标志可指定不同的字段分隔符。在 C 语言和英语语言环境下,制表符和空格字符都是空白符。

  使用排序关键字时,sort 命令首先根据第一个排序关键字的内容对所有行排序。然后,根据第二个排序关键字的内容,对所有第一个排序关键字相同的行排序,如此进行下去。按照排序关键字在命令行中出现的顺序给它们编号。如果两行对所有排序关键字的排序都相同,那么对全部行依据当前语言环境的整理顺序进行比较。

  对字段中的列进行编号时,缺省字段分隔符中的空格符将作为后继字段计数。不会将由 -t 标志指定的字段分隔符算作字段的一部分。可使用 -b 标志忽略前导空格符。


关键选项

-u选项(unique)只有唯一的记录,丢弃所有具有相同键值的记录,只留下其中的第一条。只有键值字段是重要的,也就是说:被丢弃的记录其他部分可能是不同值。和uniq命令不同的是: sort -u 消除操作是依据匹配的键值,而非匹配的记录。

重复的行:对于linux来说,repeated lines的定义是:必须相邻并且完全相同的行 

删除重复(uniqunique

uniq命令提供另一种过滤数据的方式:它常用于管道中,用来删除已使用sort排序完成, 重复记录。因为:uniq在检查重复行的时候,仅会检查相邻的行。



-k选项:-k选项的后面接着是一个字段编号,或者是一对数字,有时在-k之后可用空白分隔。每个编号后面都可以接一个点号的字符位置,以及/或修饰符字母之一。字段以及字段里的字符都是由1开始。当-k指定的域相同时,而又没有再指定其他的域,则sort会按第一个域继续排序。


-k选项的具体语法格式:

[ FStart [.CStart] ] [ Modifier ] [ , [ FEnd [. CEnd ] ] [ Modifier ] ]

其语法被其中的“,”分为两大部分,start部分和end部分。核心思想:如果不设定end部分,那么就认为end被设定为行尾。否则为End域结尾。

FStart . CStart,其中FStart表示域,而CStart表示在FStart域中的第几个字符开始算为”排序首字符”。Modifier 表示可以使用的选项:n rb d f i等(定义域内部的排序)

sort �Ct:�Ck1.2nr  file  #按第1个域的第2个字符开始排序,且按数字逆序排序(nr).

使用 -k 标志来定义排序关键字

-k KeyDefinition 标志采用下列形式:

-k [ FStart [ .CStart ] ] [ Modifier ] [ , [ FEnd [ .CEnd ] ][ Modifier ] ]

排序关键字包括所有以 FStart 变量指定的字段和 CStart 变量指定的列开头的字符及以 FEnd 变量指定的字段和 CEnd 变量指定的列结束的字符。如果未指定 Fend,就假定行的最后一个字段。如果未指定 CEnd,就假定 FEnd 字段的最后一个字符。KeyDefinition 变量中的任何字段号或列号都可以省略。缺省值为:

wKiom1OaWUKzmyROAABEnjHPY8Q461.jpg

如果这些字段间有空格,排序将他们作为分开的几个不同字段进行处理。

Modifier 变量的值可以是字母 bdfin  r 中的一个或多个。修饰符仅应用于它们连接的字段定义,与同一字母的标志有同样的效果。修饰符字母 b 仅应用于其连接的字段定义的末尾。例如:

-k 3.2b,3r

指定排序关键字,从第三字段的第二非空格列开始并扩展至第三字段结束,对这个关键字的排序以逆向整理顺序完成。如果 FStart 变量和 CStart 变量在命令行末尾以外或在 FEnd 变量和 CEnd 变量之后,那么该排序关键字被忽略。

注:一行的最大字段数为 32

标志

注:在任何排序关键字定义前出现的 -b-d-f-i-n  -r 标志应用于所有排序关键字。-b-d-f-i-n  -r 标志都不能单独出现在 -kKeyDefinition 之后;如果它们作为修饰符连接 KeyDefinition 变量,那么就只应用于连接排序关键字。


resort 机制

因为sort有一个resort重排序机制,当我们指定 -k2,2 以第2个字段进行排序,那么当第2个字段有相同的,该如何排序呢? sort 此刻会马上回到开头,从第1个字段开始排序,如果还不能排序完成,那么将跳过指定的第2个字段,对后续的字段进行排序。resort (按默认的方式排序,相当于未指定任何选项)

如果不希望这种情况出现,需要使用 -s 选项来阻止 resort 机制。

 

数值排序,跨字段的假象

默认情况下,sort 按照 str 进行排序的,要按照数值,需要 -n 选项。

sort -k2,3n   file.txt

指定 2-3字段按数值排序,结果第3字段不会干活。这是数值排序的特殊之处,只对指定的第1字段有效,除此之外的其他字段是无效的,排完指定的字段后,返回记录开头,进行 resort (按默认的方式排序,相当于未指定任何选项)当然,要阻止还是使用 -s

那如果我想对第3字段也使用数值排序呢?是的,你只有使用下面的苦逼办法,一列一列的指定。

sort -k2,2n -k3,3n file.txt

当然,如果已经能根据第2个字段进行排序了(没有相同的),那么就不会再根据第3字段排序。


数值排序(只有按数值排序才会出现这个情况,排序字符串不会)的特殊情况:

wKioL1UJhk3gq7uTAACZndJRDsU159.jpg

以第2个域的第2个字符开始到第3个域的第1个字符结束的部分进行排序。如上第2行提取10  5,第3行提取00  5,第4行提取0  3,第5行提取00  4

又因为sort认为: 0 < 00 < 000 < 0000 .

但是为什么 00  500  4前面呢?对于数值排序来说,“跨域的设定是个假象”,sort只会比较第2个域的第2个字符到第2个域的最后一个字符的部分,而不会把第3个域的开头字符纳入比较范围。


4、sort实战演练

  sort默认行为是将输入文件的每一行作为排序关键字。比较规则是从首字符向后,依次按ASCII码进行比较,最后将其按升序打印到标准输出。

 为了便于演示,准备测试文件内容如下:

[root@localhost ~]# cat names.txt
Emma Thomas:100:Marketing
Sanjay Gupta:400:Support
Alex Jason:200:Sales
Madison Randy:300:Product Development
Sanjay Gupta:400:Support
Nisha Singh:500:Sales
Emma Thomas:600:Marketing

a、对文件默认进行升序排序

[root@localhost ~]# sort names.txt
Alex Jason:200:Sales
Emma Thomas:100:Marketing
Emma Thomas:600:Marketing 
Madison Randy:300:Product Development
Nisha Singh:500:Sales
Sanjay Gupta:400:Support
Sanjay Gupta:400:Support    # 重复行

b、-u(--unique)去掉重复行

[root@localhost ~]# sort -u names.txt
Alex Jason:200:Sales
Emma Thomas:100:Marketing
Emma Thomas:600:Marketing 
Madison Randy:300:Product Development
Nisha Singh:500:Sales
Sanjay Gupta:400:Support

此时,"Sanjay Gupta:400:Support"被无情的删除了。

c、-r(--reverse)反转默认的排序方式【默认为升序,那么-r则为降序】

[root@localhost ~]# sort -r names.txt 
Sanjay Gupta:400:Support
Sanjay Gupta:400:Support
Nisha Singh:500:Sales
Madison Randy:300:Product Development
Emma Thomas:600:Marketing 
Emma Thomas:100:Marketing
Alex Jason:200:Sales

d、-n(--numeric-sort)按数值排序

  你有没有遇到过10比2小的情况。出现这种情况是由于排序程序将这些数字按字符(ASCII)来排序了,排序程序会先比较1和2,显然1小,所以就将10放在2前面喽。这也是sort的一贯作风。

  如果想改变这种现状,就要使用-n选项,来告诉sort,“要以数值来排序”!

测试文本如下:

[root@localhost ~]# cat number.txt 
10
3
87
8
5

我们对其进行升序排序:

[root@localhost ~]# sort number.txt 
10
3
5
8
87

看到没有?10竟然比3小,有没有搞错,显然这是不满足我们要求的。奇怪吧,因为默认sort是按照字符一个一个比较的。可以强制它compare according to string numerical value,来看看-n选项

[root@localhost ~]# sort -n number.txt
3
5
8
10
87

e、-t(--field-separator=SEP)指定字段分隔符

  names.txt文件的格式是以 ":" 分隔的多个字段组成,那么如果我想以第2个字段作为排序关键字进行排序,如果利用sort实现呢?

  幸好,sort提供了-t选项,后面可以设定间隔符。(是不是想起了cut和paste的-d选项,共鸣~~这就是学习linux比较困扰的地方,完成相同目的选项在不同程序中却完全不一样,坑。。),指定了间隔符之后,就可以用-k来指定字段了。

[root@localhost ~]# sort -t: -k2 names.txt 
Emma Thomas:100:Marketing
Alex Jason:200:Sales
Madison Randy:300:Product Development
Sanjay Gupta:400:Support
Sanjay Gupta:400:Support
Nisha Singh:500:Sales
Emma Thomas:600:Marketing

我们以":"作为字段分隔符,然后把第2个字段作为排序关键字,排序结果很理想。


f、其他常用选项

-f会将小写字母都转换为大写字母来进行比较,亦即忽略大小写

-c会检查文件是否已排好序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1

-M会以月份来排序,比如JAN小于FEB等等

-b会忽略每一行前面的所有空白部分,从第一个可见字符开始比较。


下面,我们进阶来一点高级的用法。

准备测试数据:

[root@localhost ~]# cat test.txt
guge 50 3000
baidu 100 5000
sohu 100 4500
google 110 5000

1、按照第2个字段进行排序

[root@localhost ~]# sort -n -k2 test.txt
guge 50 3000
baidu 100 5000
sohu 100 4500
google 110 5000

此时,出现一个问题,baidu和sohu都是100,这个时候怎么办呢?按照默认规矩,是从第一个字段开始进行升序排序,因此baidu排在了sohu前面。


2、按第2个字段排序,如果第2个字段相同则按第3个字段排序

[root@localhost ~]# sort -n -k2 -k3 test.txt
guge 50 3000
sohu 100 4500
baidu 100 5000
google 110 5000

我们加了一个-k2 -k3就解决了问题。对滴,sort支持这种设定,就是说设定域排序的优先级,先以第2个域进行排序,如果相同,再以第3个域进行排序。(如果你愿意,可以一直这么写下去,设定很多个排序优先级)


3、按第3个字段降序排序,如果相同则按第2个字段升序排序

[root@localhost ~]# sort -n -k3r -k2 test.txt 
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000

此处有使用了一些小技巧,你仔细看看,在-k3后面偷偷加上了一个小写字母r。你想想能得到答案么?揭晓:r和-r选项的作用是一样的,就是表示逆序。因为sort默认是按照升序排序的,所以此处需要加上r表示第三个域(员工平均工资)是按照降序排序。


5、sort效率

根据实际经验,在进行大文件排序时,最好对大文件进行分解,分解成N个小文件(多小呢,看机器的性能),排序完成后,再对小文件进行合并,这样会快很多。而对当个大文件的排序,sort效率很低。当然,还可以利用多线程的功能。

另外,文件太大,sort会使用部分很大的临时文件,一般在/tmp下,如果/tmp空间不够,可以使用 -T 指定一个临时文件的存放目录。

将文件split成若干小文件,然后sort每个小文件。 

 sort -m (--merge)  合并各个小文件。 sort -snm file1 file2 file3    

这篇文章非常好,举了很多例子:

http://www.cnblogs.com/51linux/archive/2012/05/23/2515299.html


你可能感兴趣的:(linux,排序,sort,文本处理)