为文本文件选择排序键时遇到问题

为文本文件选择排序键时遇到问题

假设我有一个像这样的文本文件:

e8:b4:c8:b2:d8:b9                       "Biswas Gautam"                   2016            me            Mob
ec:8e:b5:f8:a2:12                       "Dipin Gyawali"                   2015            me            Lan
f0:27:65:70:91:62                       "Karan Rai"                       2016            cs            Mob
f0:de:f1:33:33:32                       "Dipendra L. Karki"               2015            me            Lan

我想按第三列(年份)和第四列对像我这样的批次(机械工程)和 cs(计算机科学)和第三列按名称进行排序

但是第二个列名会产生问题;有时他们有一个中间名,这与我最初的解决方案相冲突。所以我认为双引号可以解决问题。但我不知道如何忽略双引号内的空格。

sort -k 4 -k 5 -k 2 -kfilename

我写了 4,因为会有 3 个空格,一个是由于列,一个是由于全名(姓名[空格]姓氏)

有什么办法可以对这样的事情进行排序吗?

答案1

如果您的文件格式严格(如图所示),您可以要求sort使用特定的列作为键:

sort -k1.75,1.78n -k1.91,1.92 -k1.105,1.107 -k1.41,1.74 input

...将样本输入转换为:

f0:de:f1:33:33:32                       "Dipendra Karki"                  2015            me            Lan
ec:8e:b5:f8:a2:12                       "Dipin Gyawali"                   2015            me            Lan
f0:27:65:70:91:62                       "Karan Rai"                       2016            cs            Mob
e8:b4:c8:b2:d8:b9                       "Biswas Gautam"                   2016            me            Mob

答案2

$ sed -E 's/ {3,}/@/g' file | sort -t @ -k3,3 -k5,5 | sed 's/@/    /g'
ec:8e:b5:f8:a2:12    "Dipin Gyawali"    2015    me    Lan
f0:de:f1:33:33:32    "Dipendra Karki"    2015    me    Lan
e8:b4:c8:b2:d8:b9    "Biswas Gautam"    2016    me    Mob
f0:27:65:70:91:62    "Karan Rai"    2016    cs    Mob

这会将任何三个或更多空格替换为该字符@(数据中不存在的任何字符都将起作用)。

然后,sort指示将其输入解释为@- 分隔字段,并按第三个字段(年份)和第五个字段(设备)进行排序。最后用四个空格替换排序数据中的sed每个空格(您可以选择在此处插入文字制表符,或使用 GNU )。@\tsed

更漂亮:

$ sed -E 's/ {3,}/@/g' file | sort -t @ -k3,3 -k5,5 | column -s @ -t
ec:8e:b5:f8:a2:12  "Dipin Gyawali"   2015  me  Lan
f0:de:f1:33:33:32  "Dipendra Karki"  2015  me  Lan
e8:b4:c8:b2:d8:b9  "Biswas Gautam"   2016  me  Mob
f0:27:65:70:91:62  "Karan Rai"       2016  cs  Mob

以下用于awk将每列格式化为左对齐的 20 个字符宽的字符串:

$ sed -E 's/ {3,}/@/g' file | sort -t @ -k3,3 -k5,5 | awk -F@ '{ for (i=1;i<=NF;++i) printf("%-20s",$i); print "" }'
ec:8e:b5:f8:a2:12   "Dipin Gyawali"     2015                me                  Lan
f0:de:f1:33:33:32   "Dipendra Karki"    2015                me                  Lan
e8:b4:c8:b2:d8:b9   "Biswas a Gautam"   2016                me                  Mob
f0:27:65:70:91:62   "Karan Rai"         2016                cs                  Mob

或者,您可以单独格式化它们:

$ sed -E 's/ {3,}/@/g' file | sort -t @ -k3,3 -k5,5 | awk -F@ '{ printf("%s %-30s %-30s %-30s %s\n", $1,$2,$3,$4,$5) }'
ec:8e:b5:f8:a2:12 "Dipin Gyawali"                2015                           me                             Lan
f0:de:f1:33:33:32 "Dipendra Karki"               2015                           me                             Lan
e8:b4:c8:b2:d8:b9 "Biswas a Gautam"              2016                           me                             Mob
f0:27:65:70:91:62 "Karan Rai"                    2016                           cs                             Mob

答案3

如果您想精确保留间距(而不是重新格式化它,就像拘萨罗南达的回答)并且不想依赖于确切的列位置(例如杰夫·夏勒的回答),像这样的 Perl 脚本就可以工作:

#!/usr/bin/perl
use 5.022;

my @dat;
while (<<>>) {
    #          0     1      2        3     4     5     6     7     8
    #          mac   sp     name     sp    yr    sp    dpt   sp    net
    my @m = /^(\S+) (\s+) "([^"]+)" (\s+) (\S+) (\s+) (\S+) (\s+) (\S+)$/x
        or die "invalid line: $_";
    push @dat, \@m;
}

@dat = sort {
    $a->[4] <=> $b->[4] || $a->[6] cmp $b->[6] || $a->[2] cmp $b->[2]
} @dat;

foreach (@dat) {
    print join('', @$_), "\n";
}

请注意,使用 Perl v5.22.0 或更高版本作为<<>>运算符;如果你使用它的话,它应该可以与 Perl 的古老版本一起使用<>。该程序本质上有三个“段落”:第一个使用正则表达式解析行,还捕获所使用的确切间距。第二段对数据进行排序。第三段将其打印回来。

答案4

另一种方法是替换每个空格字符之间用占位符 @ 引起引号,然后进行排序,然后将 @ 字符更改回空格:

perl -pe 's#("[^"]*")#$1 =~ s/ /@/rg#eg' filename | sort -k 3 -k 4 -k 2 | sed 's/@/ /g'

相关内容