我有一个这样的日志文件:
12 adsflljl
12 hgfahld
12 ash;al
13 a;jfda
13 asldfj
15 ;aljdf
16 a;dlfj
19 adads
19 adfasf
20 aaaadsf
我想把它们“分组”,就像这两个中的一个:
12 adsfllj, 12 hgfahld, 12 ash;al
13 a;jfda, 13 asldfj
15 ;aljdf
16 a;dlfj
19 adads, 19 adfasf
20 aaaadsf
或
12 adsfllj, hgfahld, ash;al
13 a;jfda, asldfj
15 ;aljdf
16 a;dlfj
19 adads, adfasf
20 aaaadsf
我完全陷入困境。如果vim没有这样做,我也有sed和awk以及bash。我只是不想写一个bash脚本,我想增加我的正则表达式
答案 0 :(得分:6)
在Vim中你可以使用:
:%s/\(\(\d\+\) .*\)\n\2/\1, \2/g
表示:如果在新行后匹配一组数字,请删除换行符并改为使用逗号。如果您不熟悉它们,则\1
和\2
会反向引用。
不幸的是,这只会一次合并两次,所以在实现目标之前,你必须多次运行它。
编辑:一次性完成它的一种方法是循环并利用这样一个事实:一旦文件不再匹配,就会发出错误。虽然这个错误有点烦人,但是我用单行代码做得不好:
:while 1 | :%s/\(\(\d\+\) .*\)\n\2/\1, \2/g | :endwhile
答案 1 :(得分:5)
我只是使用awk:
awk '
{
sep = val[$1] ? ", " : ""
val[$1] = val[$1] sep $2
}
END {for (v in val) print v, val[v]}
' log.file | sort > new.file
答案 2 :(得分:2)
在Vim中,我会使用命令
:g/^\d\+/y|if+@"==getline(line('.')-1)|s//,/|-j!
如果可以保证第一列始终包含数字ID。
否则,我会修改if-condition如下。
:g/^\S\+/y|if matchstr(@",@/)==matchstr(getline(line('.')-1),@/)|s//,/|-j!
答案 3 :(得分:1)
另一种方法,这次使用宏(我建议你使用另一个解决方案,这个只是表明有很多方法可以做到这一点):
gg:%s/$/,
输入qa0V?
CTRL-R CTRL-W\>\&^
输入Jjq100@a:%s/.$//
返回
说明:
gg
=>转到文件开头:%s/$/,
=>将逗号添加到每一行qa
=>开始将宏录制到寄存器a
0V
=>转到第一列,然后开始行选择?
=>向后查找(你必须有set wrapscan
)
\>
确保结束\&^
确保在行首时匹配模式。您不能将^
放在模式的开头,因为如果设置了incsearch
,那么只要您键入^
,然后 ctrl-r ctrl-w 将打印光标下的单词,该单词将移至上一行。J
会将视觉选择中的所有行与空格连接起来。j
将转到下一行q
将停止录制宏100@a
将播放100次。:%s/.$//
将删除尾随逗号。答案 4 :(得分:0)
我不认为在这里使用正则表达式是个好主意。您可以在@glenn中找到与vimscript编写的解决方案相同的想法如下:
function JoinLog()
let d={}
g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') |
\let d[k]=get(d, k, [])+[t]
%delete _
call setline(1, map(sort(keys(d)), 'v:val." ".join(d[v:val], ", ")'))
endfunction
您可以保留订单而不是排序:
function JoinLog()
let d={}
let ordered=[]
g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') |
\if has_key(d, k) | let d[k]+=[t] |
\else | let ordered+=[k] | let d[k]=[t] |
\endif
%delete _
call setline(1, map(copy(ordered), 'v:val." ".join(d[v:val], ", ")'))
endfunction