Vim查找替换的高端用法

处理文本时,有这么一个需求:一个包含大量聊天记录的文本文件,有两列。一列是时间,一列是内容。大致如:

1319265479, 你好啊。
...

现要将第一列的Unix时间戳转换成”2011-10-22 14:37:59″这种形式。

我第一反应就是用Vim。但发现,每一行的时间戳都不一样,不能直接简单的查找替换。其次,使用sed来处理每行,使用某个程序来将时间戳转换成直观的时间。但据我查找,Linux下的date可以将时间戳转成直观的时间,但Mac OS X下的date却不能做到。再次,我只好回到Vim里,打算从文档看看查找替换时能不能支持函数来转换匹配的字符串,结果还真有。

因为这文件里的时间戳已经很有规律了,都是以131开头的,所以我就用简单的匹配来做了。

:%s/\(131\d+\),/\=strftime("%Y-%m-%d %H:%I:%S", submatch(1)) . ", "/g

下面简要解释下这个正则表达式。:表示进入Vim的命令模式,%s表示进行全局替换,\(131\d+\),表示匹配以逗号结尾的时间戳,\=strftime()表示将先执行函数strftime(),将返回的结果进行替换,submatch(1)表示将匹配的时间戳当作strftime()的参数,.”,”表示将strftime()的结果与”,”组合起来(.号表示连接两个字符串)。这个正则表达式应用在文首的例文,进行展开后的结果如下:

:%s/1319265479,/\=stftime("%Y-%m-%d %H:%I:%S", 1319265479).","/g
等同于:
:%s/1319265479,/2011-10-22 14:37:59,/g

关于这种用法,可以Vim中使用

:h sub-replace-\=

来查看相关文档。

Vim编辑远程文件

Vi/Vim作为我最值得炫耀的编辑器(没有之一),与它的对手Emacs相比,经常被提到的一个优点就是:这个世界上几乎所有的*nix服务器,都会自带Vi/Vim。这个优点带来的方便之处在于,只要会用Vi/Vim,你就可以在任何一台*nix服务器上使用神乎奇技的手法来编辑文件,而不用额外安装软件(Emacs去哭吧)。这个优点几乎在我所有的服务器操作中都用到过,但在今天,似乎略有不便。

我有一台VPS,这基本上是众所周知的事情了。但中美网速的差别与两国国民生活水平一样大,导致我娴熟的指头在服务器上用Vim直接修改文件时,遇到了十分强烈停顿感。这深深地挫伤了我文思如泉涌的灵感。便想把远程文件下载到本地,修改后再上传,就像Emacs一直以来无奈的做法一样。当然这事必须不能手工去做,不然不仅不能给Vimer长脸,还会被认为是其它阵营派来给Vim抹黑的。

本文的主角是netrw.vim,它可以让Vim直接编辑网络上的文件(当然是netrw已经把该文件下载到临时文件里了),并且还支持保存到网络上(如果有写权限的话)。不知道从什么开始,它便成为Vim默认自带的插件了。在大多数现代GNU/Linux发行版里,安装Vim时都会顺便把它给装上了。

netrw使用起来也很简单。在终端里可以直接:

$ vim ftp://user@some_url/path/filename

这样便可以编辑一个远程文件,其它操作,便与编辑本地文件无异,该’:w’或’:q’时都悉听尊便。

但并不是所有的远程文件都能这么方便的访问,像我今天修改的那个文件,只有通过ssh才能访问到,并且ssh还是有key认证的。但是呢,对于我而言丝毫不影响,因为我还可以这样:

$ vim scp://t@tigerlee.me/path/target_file

也许你这样做,并不会成功,当然不成功的原因是多样的,最主要原因可能是你不能顺利地(不用输入密码,没有任何交互的提示)使用

$ scp://t@tigerlee.me/path/target_file .

来将target_file拷贝到本地。如果能做到顺利,那么你也会成功的。要实现顺利地使用scp,无外乎就是去掉一些不必要的认证过程,使用不带密码的key是最方便的了。详细使用参见netrw的文档:‘netrw-ssh-hack’,有Vim的哥们呢,就直接使用’:h netrw-ssh-hack’来看好了。

当远程文件拥有一个冗长的路径时,直接输入全部路径可能比较麻烦,因为它又不能使用Tab补全。这时可以先输入:

$ vim scp://t@tigerlee.me/

然后再在稍后出现的路径列表里慢慢选了。

netrw共支持三种使用模式:远程编辑,远程浏览(包括浏览目录内容),远程写入。不同的模式,支持的协议也不同。这是理所当然了,不然对于HTTP地址的文件,怎么写入啊?以下是不同模式支持的协议:

远程编辑
:e dav://machine[:port]/path                    uses cadaver
:e fetch://[user@]machine/path                  uses fetch
:e ftp://[user@]machine[[:#]port]/path          uses ftp   autodetects <.netrc>
:e http://[user@]machine/path                   uses http  uses wget
:e rcp://[user@]machine/path                    uses rcp
:e rsync://[user@]machine[:port]/path           uses rsync
:e scp://[user@]machine[[:#]port]/path          uses scp
:e sftp://[user@]machine/path                   uses sftp

远程浏览
:Nread ?                                        give help
:Nread "machine:path"                           uses rcp
:Nread "machine path"                           uses ftp   with <.netrc>
:Nread "machine id password path"               uses ftp
:Nread "dav://machine[:port]/path"              uses cadaver
:Nread "fetch://[user@]machine/path"            uses fetch
:Nread "ftp://[user@]machine[[:#]port]/path"    uses ftp   autodetects <.netrc>
:Nread "http://[user@]machine/path"             uses http  uses wget
:Nread "rcp://[user@]machine/path"              uses rcp
:Nread "rsync://[user@]machine[:port]/path"     uses rsync
:Nread "scp://[user@]machine[[:#]port]/path"    uses scp
:Nread "sftp://[user@]machine/path"             uses sftp

远程写入
:Nwrite ?                                       give help
:Nwrite "machine:path"                          uses rcp
:Nwrite "machine path"                          uses ftp   with <.netrc>
:Nwrite "machine id password path"              uses ftp
:Nwrite "dav://machine[:port]/path"             uses cadaver
:Nwrite "ftp://[user@]machine[[:#]port]/path"   uses ftp   autodetects <.netrc>
:Nwrite "rcp://[user@]machine/path"             uses rcp
:Nwrite "rsync://[user@]machine[:port]/path"    uses rsync
:Nwrite "scp://[user@]machine[[:#]port]/path"   uses scp
:Nwrite "sftp://[user@]machine/path"            uses sftp
http: not supported!

小总结一下:

  • 网速慢,文件小的情况下,可以使用此法编辑远程文件。
  • 网速快,文件大的情况下,那就远程编辑文件吧。
  • 网速慢,文件大或网速快,文件小的情况下,你应该明白怎样做吧?

vim常用小技巧[不断更新]

我是vim控~~~

  • 删除文本文件中的重复行:”awk ‘!a[$0]++’ filename”
  • 在VIM中删除文本文件中的重复行还可以:”:sort u”,只支持vim 7以上的版本。
  • 在VIM中替换文本时,使用正则表达式可以用\n来匹配换行符,但需要将某一字符替换为换行符时,如”%s/string/string\n/g”,所有string会变成string^@,而不是string+换行,这时需要使用”%s/string/string^M/g”来表示换行符。”^M”的输入为:ctrl+v, ctrl+m。