我有一组 POSIX 正则表达式*
^BEGIN:VCARD\r$
^VERSION[^A-Z]
^FN[^A-Z]
^N[^A-Z]
^NICKNAME[^A-Z]
^EMAIL[^A-Z]
^X-\([A-Z-]*\)
^TEL[^A-Z]
^ADR[^A-Z]
^ORG[^A-Z]
^TITLE[^A-Z]
^BDAY[^A-Z]
^URL[^A-Z]
^ROLE[^A-Z]
^NOTE[^A-Z]
^END:VCARD\r$
以及一个文件,其中每行都与正则表达式之一匹配:
BEGIN:VCARD
VERSION:3.0
N:Doe;Jane;;Ms;
URL:http://janedoe.com/
EMAIL:[email protected]
EMAIL:[email protected]
BDAY:1970-01-01
X-JABBER:[email protected]
X-ICQ:1234567890
END:VCARD
我想根据以下内容对这些行进行排序
- 正则表达式匹配的行号(以便以 FN 开头的行位于以 N 开头的行之前),
- 匹配组(以便 X-ABC 位于 X-DEF 之前)
理想情况下,不应对行的其他部分进行排序(因此应保留以 EMAIL 开头的行序列)。因此,预期结果应该是:
BEGIN:VCARD
VERSION:3.0
N:Doe;Jane;;Ms;
EMAIL:[email protected]
EMAIL:[email protected]
X-ICQ:1234567890
X-JABBER:[email protected]
BDAY:1970-01-01
URL:http://janedoe.com/
END:VCARD
有现有的工具可以做到这一点吗?
编辑:实施结果基于拉尔斯·罗尔巴赫的回答。
* 这是 Gmail 联系人导出文件中 vCard 属性的序列。
答案1
通常的sort
命令不提供指定特定“字典”的包含方式,虽然该grep
命令允许您提供正则表达式文件,但它不会更改输出的顺序。但是您可以将两者放在一个简单的foreach
循环中 - 这是一个在 bash shell 中运行的示例:
for i in `cat fileofregexp`; do grep "$i" myinputfile; done
这将从正则表达式文件中逐一获取每个正则表达式行,并输出输入文件中的任何匹配项,因此生成的输出将按正则表达式顺序排序。请注意,输入文件中完全不匹配的任何行都不会出现在输出中。
附录:根据要求,这是一个使用循环的版本while
:
while IFS= read -r i; do grep "$i" myinputfile; done < fileofregexp
答案2
这并不完全是您的框架方式,但考虑到您的实际目的,抓住冒号之前的部分并按其排序会更简单。下面是一个 Perl 脚本,它将来自单独的排序键的行累积到单独的数组条目中,并在到达末尾时刷新 vcard。
#!/usr/bin/perl -n
BEGIN {
@headers = qw(BEGIN VERSION FN N NICKNAME EMAIL X- TEL ADR ORG TITLE BDAY URL ROLE NOTE END);
for $h (@headers) { $data{$h} = ""; }
}
if (/^([^:]+):/) {
$data{exists $data{$1} ? $1 : "X-"} .= $_;
if ($1 eq 'END') {
for $h (@headers) { print $data{$h}; $data{$h} = ""; }
}
} else {
print;
}
如果您确实想要完整的正则表达式灵活性,请迭代正则表达式,而不是在哈希中查找键。
#!/usr/bin/perl -n
BEGIN {
@regexps = qw(^BEGIN:VCARD\r$ ^VERSION[^A-Z] ^FN[^A-Z] ^N[^A-Z] ... ^END:VCARD\r$);
for $r (@regexps) { $data{$r} = ""; }
}
for $r (@regexps) {
next unless $_ =~ $r;
$data{$r} .= $_;
last;
}
if ($_ =~ $regexps[@regexps-1]) {
for $r (@regexps) { print "++", $data{$r}; $data{$r} = ""; }
}