根据文件内容的第三个字段对文件进行排序。

这是文件的内容
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000    #Q  2
我想按第三个字段($3)排序。
08/03/2015 09:35:15.934
有人可以帮忙吗。

日期的格式是什么?以日、月、年的顺序吗? - A.B.
3个回答

使用 sortawk

假设您的日期格式如下:

08/03/2015 –> 日,月,年

使用以下命令。

  • 这是一个命令(注意行尾的\
  • your_input_file替换为您的文件名。

该命令

awk '{for (i=1;i<=NF;i++) {if (i==3) {printf "%s/%s/%s\t",substr($3,7,4),substr($3,4,2),substr($3,1,2)} else {printf "%s\t",$i}} printf "\n"}' your_input_file |\
sort -k3 -k4 |\
awk '{for (i=1;i<=NF;i++) {if (i==3) {printf "%s/%s/%s\t",substr($3,9,2),substr($3,6,2),substr($3,1,4)} else {printf "%s\t",$i}} printf "\n"}'

故障

  • 第一个 awk 命令将日期从 day/month/year

    08/03/2015
    

    更正为 year/month/day

    2015/03/08
    
  • sort 命令对新结构进行排序

  • 第二个 awk 对第一个 awk 所做的更改进行撤销


如果您的日期格式不是“日/月/年”,您需要更改以下部分:
  • substr($3,7,4),substr($3,4,2),substr($3,1,2)

  • substr($3,9,2),substr($3,6,2),substr($3,1,4)

说明: substr(字段,起始位置,长度)
  • 字段

    请勿更改,它是第三列

  • 起始位置长度

    起始位置开始返回一个长度为长度的子字符串


示例

输入文件foo

cat foo

090100010000481074      1       08/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       03/07/2016 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2

输出结果为:

awk '{for (i=1;i<=NF;i++) {if (i==3) {printf "%s/%s/%s\t",substr($3,7,4),substr($3,4,2),substr($3,1,2)} else {printf "%s\t",$i}} printf "\n"}' foo | sort -k3 -k4 | awk '{for (i=1;i<=NF;i++) {if (i==3) {printf "%s/%s/%s\t",substr($3,9,2),substr($3,6,2),substr($3,1,4)} else {printf "%s\t",$i}} printf "\n"}'

090100010000481074  1   07/03/2015  09:35:15.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   07/03/2015  09:35:17.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   08/03/2015  09:35:15.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   08/03/2015  09:35:15.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   08/03/2015  09:35:17.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   07/03/2016  09:35:17.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   08/03/2016  09:35:17.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  2   
090100010000481074  1   03/07/2016  09:35:15.934    LA150803000AJSX00000    LA150803000AJSX CRBP    Buy ELF 100 1980000 119 3   2   1890000 119 100 2040000 119 100 1980000 119 1000    #Q  

你忘了一个事实,你需要先恢复日期格式。这样排序日期是不正确的。 - Jacob Vlijm
@JacobVlijm 哦,你是指2015年03月08日吗? - A.B.
没错 :) 否则它会按照日/月/年的顺序排序,而不是年/月/日。 - Jacob Vlijm
@JacobVlijm 完成了 :) - A.B.
非常好用! - Jacob Vlijm
如果你喜欢我的回答,就点击我回答左侧的小灰色☑,将其变成美丽的绿色。如果你不喜欢我的回答,点击小灰色的下箭头,如果你真的非常喜欢我的回答,点击小灰色的勾号和小上箭头...如果你有任何进一步的问题,请访问http://askubuntu.com/questions/ask。 - A.B.

假设08/03/2015代表的是2015年3月8日,你可以使用这个bash一行命令:
while IFS= read -r line; do parts=( $(echo "$line") ); printf '%s %s\n' "$(date --date="$(sed -r 's_([^/]+/)([^/]+/)_\2\1_' <<<"${parts[2]} ${parts[3]}")" '+%s')" "$line"; done <file.txt | sort -k1,1n | cut -d' ' -f2-
展开形式:
while IFS= read -r line; do 
    parts=( $(echo "$line") ) 
    printf '%s %s\n' "$(date --date="$(sed -r 's_([^/]+/)([^/]+/)_\2\1_' <<<"${parts[2]} ${parts[3]}")" '+%s')" "$line" 
done <file.txt | sort -k1,1n | cut -d' ' -f2-
我们正在读取输入文件的每一行,并将其放入变量line中。 parts数组将包含以空格分隔的line的不同部分。 然后,我们提取相关日期时间字段并使用sed将其设置为正确的格式,获取相应的时代时间。 在循环中,我们首先输出时代时间,然后是原始的line。 现在,由于我们首先有了时代时间,我们可以使用sort按数字顺序对数据进行排序,根据第一个字段排序。 最后,我们删除了时代时间,得到了我们的最终输出。 示例(来自@A.B.):
$ cat file.txt 
090100010000481074      1       08/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       03/07/2016 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2


$ while IFS= read -r line; do parts=( $(echo "$line") ); printf '%s %s\n' "$(date --date="$(sed -r 's_([^/]+/)([^/]+/)_\2\1_' <<<"${parts[2]} ${parts[3]}")" '+%s')" "$line"; done <file.txt | sort -k1,1n | cut -d' ' -f2-
090100010000481074      1       07/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2015 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       07/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       08/03/2016 09:35:17.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
090100010000481074      1       03/07/2016 09:35:15.934 LA150803000AJSX00000    LA150803000AJSX CRBP    Buy     ELF     100     1980000 119     3       2       1890000 119     100     2040000 119     100     1980000 119     1000   #Q  2
另一方面,如果"08/03/2015"表示"2015年8月3日",您可以使用(无需使用"sed"来获取"date"能够理解的正确格式):
while IFS= read -r line; do parts=( $(echo "$line") ); printf '%s %s\n' "$(date --date="${parts[2]} ${parts[3]}" '+%s')" "$line"; done <file.txt | sort -k1,1n | cut -d' ' -f2-

展开形式:

while IFS= read -r line; do 
    parts=( $(echo "$line") ) 
    printf '%s %s\n' "$(date --date="${parts[2]} ${parts[3]}" '+%s')" "$line" done <file.txt | sort -k1,1n | cut -d' ' -f2-

sed这部分非常好 =) - A.B.
@A.B. 我最初以为输入格式是 mm/dd/yy,然后看到你的示例发现格式是 dd/mm/yy,所以必须使用 sed 来使输入正确地格式化为 date :) - heemayl
好的,谢谢。但是有没有办法让我检查一下文件是否按照第三个字段正确排序,这样我就不需要手动操作了? - canonical
@canonical GNU date 不理解格式 dd/mm/yy(例如 27/12/2015),它将其视为 mm/dd/yy。因此,由于您有一个以 dd/mm/yy 格式表示的日期 08/03/2015,我们需要先使用 sed 将其转换为 mm/dd/yy03/08/2015)。所以如果您的日期是以 mm/dd/yy 格式表示的,则无需进行转换,这样会更简单。 - heemayl
@heemayl 不,格式是月/日/年...它是8月3日。不需要转换...有没有脚本可以在我自己尝试排序之前检查文件是否已经正确排序? - canonical
@canonical 那么,现在就容易多了:while IFS= read -r line; do parts=( $(echo "$line") ); printf '%s %s\n' "$(date --date="${parts[2]} ${parts[3]}" '+%s')" "$line"; done <file.txt | sort -k1,1n | cut -d' ' -f2- ..还要注意的是,任何检查是否排序的操作都需要相同的读取文件和检查过程..因此,这个一行命令可以同时解决这两个问题,如果它们已经排序好了,那就没问题,如果没有排序,它们将会被排序.. - heemayl
@canonical请检查我的编辑。 - heemayl
@heemayl 我想打印时间偏移的那一行...不要将其排序回去...我想打印未排序的行 - canonical

很好的问题。 你问题的主要问题是你实际上想按照两个字段(日期、时间)进行排序,其中一个需要在排序之前先反转(从后往前读取),因为日期的格式是dd/mm/yyyy。 如果我们以详细的方式来做这件事情,下面的脚本可以实现。它: - 首先反转日期,将其与时间字段结合,创建一个元组并包含行索引。 - 按照日期/时间对元组列表进行排序,并按照排序后列表的新顺序打印出原始行。 脚本如下:
#!/usr/bin/env python3
import sys

f = open(sys.argv[1]).readlines()
rawlist = []

for i, l in enumerate(f):
    # split the line, read the date backwards for correct sorting, since it is dd/mm/yyyy now
    # add the time
    l = l.split(); cr = (i, l[2].split("/")[::-1]+l[3].split(":"))
    rawlist.append(cr)
# sort by date, time
rawlist.sort(key=lambda x: x[1])
# print the lines by found (sorted) indexes
for i in [d[0] for d in rawlist]:
    print(f[i], end = "")
如何使用
  • 将脚本复制到一个空文件中,保存为sort_byfield.py
  • 使用文件作为参数运行它:

    python3 /path/to/sort_byfield.py <file>
    

示例

列表(为了清晰起见,省略了一些字段):

090100010000481074      1       08/03/2014 09:35:17.932 
090100010000481074      1       07/03/2015 08:22:15.934 
090100010000481074      1       07/03/2015 09:55:15.933 
090100010000481074      1       08/03/2013 09:01:15.934
090100010000481074      1       08/03/2013 08:35:15.934
然后输出:
090100010000481074      1       08/03/2013 08:35:15.934
090100010000481074      1       08/03/2013 09:01:15.934
090100010000481074      1       08/03/2014 09:35:17.932 
090100010000481074      1       07/03/2015 08:22:15.934 
090100010000481074      1       07/03/2015 09:55:15.933 

简化版

如果我们不太关心可读性,下面的版本是同一脚本的简化版:

#!/usr/bin/env python3
import sys

f = open(sys.argv[1]).readlines()
rawlist = [(i, l.split()[2].split("/")[::-1]+l.split()[3].split(":")) for i, l in enumerate(f)]
rawlist.sort(key=lambda x: x[1])
[print(f[i], end = "") for i in [d[0] for d in rawlist]]