我有一个日志文件,例如:
Bug123:c:SomeComment
Bug222:c:SomeOtherComment
Bug123:c:SecondComment
我需要制作:
Bug123
SomeComment
SecondComment
Bug222
SomeComment
使用 bash,我似乎想不出一种简单的方法来完成此任务。有任何想法吗?
答案1
$ awk 'BEGIN {FS=":"} {comments[$1][NR]=$3} END {for (bug in comments) { print bug; for (comment in comments[bug]) { print " ",comments[bug][comment] } } } ' /path/to/input
Bug123
SomeComment
SecondComment
Bug222
SomeOtherComment
这是通过在读取输入文件时设置一个多维数组,然后按照看到“错误”的顺序遍历结果数据来实现的。通过一些小的调整,可以修改它以对外层数组进行排序。
答案2
这是一种有趣的方法vi
(或者,实际上,ex
如果您省略每个命令的前导冒号):
:%!awk -F: '{a[$1];print} END {for (i in a) {print i}}'
:v/:/m0
:%!sort -st: -k1,1
:%s/^.*:/\t/
解释:
Awk 命令按原样打印每一行,并在文件末尾转储所有唯一第一个字段的列表。
该v
命令m
将所有行移至第 0 行(文件的开头)不有任何冒号。 (换句话说,是从 输出的第一个字段的列表awk
。)
该sort
命令对s
表进行排序,保留行的顺序,而不是根据第一个字段排列它们。 (我们已经将“标题”字段移至文件的开头。)
然后s
ubstitute 命令将每个非标题行的前缀变成制表符。
您提供的输入的结果:
Bug123
SomeComment
SecondComment
Bug222
SomeOtherComment
附加说明:
:
(冒号)是您键入vi
以开始ex
-style 命令的内容。
%
是一个地址范围ex
。它的意思是“将以下操作/命令应用于缓冲区(文件)中的所有行。”
当与地址一起使用时,!
开始“过滤器” ex
:地址指定的行作为输入馈送到指定的外部命令,并在缓冲区中被该命令的输出替换。
-F:
设置 Awk 字段分隔符。
{}
(花括号)在 Awk 中用于包围要运行的命令。既然没有awk花括号前面的地址(单引号内),花括号中的操作将应用于每一行输入。
a[$1]
使用当前行的第一个字段 ( $1
) 作为索引创建一个数组元素。因为它没有说= "whatever"
,所以数组元素没有价值,但这并不重要;我们只希望数组包含该元素。
;
终止该 Awk 命令。
print
是打印当前行(默认情况下)或传递的任何参数(如后面所示print i
)的 Awk 命令。
END
标记在处理所有输入(到 Awk)后要执行的操作块(在大括号中)。
该for
循环打印名为 的数组的所有索引a
。这是日志文件中删除重复的第一个字段,没有特定的顺序。
关于 Awk 命令就讲这么多。
该v
命令是与全局命令ex
相反的命令g
。 g
执行一个动作所有行匹配某种模式。 v
对不匹配给定模式的所有行执行指定的操作。
/
开始和结束模式。就:
在这种情况下。
m
意思是“移动”。因此,:v/:/m0
将vi
所有不包含任何冒号的行移动到文件顶部。
其余的命令应该很清楚。 :)
答案3
Perl 一行
perl -MList::Util=uniq -F: -lane '
push @keys, $F[0];
push @{ $comment{$F[0]} }, $F[2];
} END {
for $key (uniq @keys) {
print $key;
print " $_" for @{$comment{$key}};
}
' file