作为一个开发或运维人员,登录到服务器之后,最常用的编辑器之一就是vim。使用vim的过程中经常遇到一个尴尬的情况,
拷贝了一段内容后,切换到其他窗口想要粘贴,却意外的发现并不是自己想要的内容。
重复两次后,才意识到刚才是在vim中拷贝的,拷贝的这些内容是无法进入你本地电脑的粘贴板的。
网上有很多相关的解决方案,但或多或少都有配置麻烦或根本没有效果。我经过一番摸索,也形成了自己的解决方法,
不过这个方法暂时只能在Mac系统上使用。我自己也使用了一段时间,觉得还不错,配置也很简单,不需要安装任何软件,期望对你所有帮助。
方案
总体方案如下图所示:
方案分成三个部分:
- 在vim中执行拷贝的时候,我们要做些额外的事情,就是将其发送回本地;这个通过
autocmd+TextYankPost
实现;
- 将拷贝的内容发送回本机需要一个数据传输通道,登录服务器用的最多的就是ssh,ssh提供的
RemoteForward
就可以满足这个需求。
- 将传送回本机的内容复制到系统的粘贴板中,这个通过mac系统上的
pbcopy
可以实现。
方案说完了,下面开始配置吧。
配置
配置ssh
修改你本地的ssh配置文件~/.ssh/config
,在这个文件中添加如下的配置:
1
2
|
Host *
RemoteForward 22222 127.0.0.1:22222
|
添加上面的配置后,在终端中通过ssh登录服务器时,就会建立一个额外的数据通道。
ssh的服务器端会启动一个进程并在22222
端口监听,并将收到的数据发送回你本机的22222端口。
有的服务器的ssh配置不允许端口转发,所以,配置好之后,你最好登录服务器确认一下。
1
|
$ netstat -ntlp | grep -w 22222
|
如果输出不为空,表示可以建立连接。如果为空,需要修改ssh的配置。
如果你使用的是有图形界面的ssh客户端,则需要在相应的地方配置端口转发。
配置服务器上的vim
方案一:使用vim channel
在vim的配置文件中添加如下的内容:
1
2
3
4
5
6
7
8
9
10
11
12
|
function! s:send2net()
let ch = ch_open('127.0.0.1:22222')
if ch_status(ch) != "open"
echomsg "Failed to open channel, status = " .. ch_status(sh)
endif
call ch_sendraw(ch, getreg('"'))
endfunction
augroup send2net
au!
au TextYankPost * if v:event.operator ==# 'y' | call s:send2net() | endif
augroup END
|
方案二:使用自己编写的脚本
如果你的vim不支持channel
,还一个可选的方法,自己编写脚本替代,同时改下vimrc的配置。
具体如下,首先执行下面的命令
1
2
3
4
5
6
7
8
9
10
11
|
$ cat > /usr/local/bin/pbcopy << END
#!/bin/bash
if [ $# -eq 1 ]; then
cat $1 | nc localhost 22222
else
[[ -p /dev/stdin ]] && { cat | nc localhost 22222; }
fi
END
$ chmod +x /usr/local/bin/pbcopy
|
上面的命令会在/usr/local/bin
目录下,创建一个名为pbcopy
的脚本,并赋予可执行权限。
接着将如下的内容添加到vim的配置文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
let s:selfpid = getpid()
function! s:pbcopy()
let l:path = printf("/tmp/%d.pbcopy", s:selfpid)
let l:command = printf("/usr/local/bin/pbcopy %s", l:path)
call writefile(split(getreg('"'), "\n", 1), l:path)
call system(l:command)
endfunction
function! s:cleanup()
let l:path = printf("/tmp/%d.pbcopy", s:selfpid)
let l:command = printf("/bin/rm -f %s", l:path)
call system(l:command)
endfunction
augroup pbcopy
au!
au TextYankPost * if v:event.operator ==# 'y' | call s:pbcopy() | endif
au VimLeave * call s:cleanup()
augroup END
|
上面这段配置在vim执行拷贝后,会先将拷贝的内容写入一个临时文件,然后调用pbcopy
这个脚本,将拷贝的内容发送回本机。
配置Mac系统
通过步骤1、2,可以实现将拷贝的内容传递回本机,这之后如何将这些内容拷贝到系统粘贴板呢?这就需要配置你的Mac了。具体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# 创建一个plist文件
$ cat > $HOME/Library/LaunchAgents/pbcopy.plist << END
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>localhost.pbcopy</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/pbcopy</string>
</array>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockServiceName</key>
<string>22222</string>
<key>SockNodeName</key>
<string>127.0.0.1</string>
</dict>
</dict>
</dict>
</plist>
END
# 加载配置
$ launchctl load -w $HOME/Library/LaunchAgents/pbcopy.plist
# 检查配置
$ launchctl list | grep localhost.pbcopy
- 0 localhost.pbcopy
|
通过上面的配置,launchd
启动一个进程监听127.0.0.1:22222
,并将其收到的内容传递给系统粘贴板。
我们通过ssh传送回来的内容正好是发送到本机的127.0.0.1:22222
这个地址,这样整个拼图的最后一块就补齐了。
测试
通过ssh登录服务器,然后打开vim,在vim中通过V
选择一段文本,然后按下y
,或者直接用yy
复制一行的内容,
然后切换到本机的其他应用程序,试试cmd+v
,应该可以看到刚才拷贝的内容了。
从此,不管是本地,还是远程,都可以愉快的复制、粘贴了。
有点遗憾的是,这个方法暂时只能在Mac系统上使用。
参考资料
- https://gist.github.com/andyshinn/dd6cabc38194e57d4014d291a03a1995
- https://gist.github.com/ljjjustin/f99171b337213d5e00de6ac0c87b631b