假设我有一个像这样的文本文件:
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 )。@
\t
sed
更漂亮:
$ 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'