假设我有一个挂起的X11服务器,这使我无法保存X11服务器控制的XTerm Vim会话中的工作。(不是GVim,只是常规的Vim-in-XTerm。)
有没有办法(从另一个终端)告诉正在运行的Vim进程从命令行“全部保存并退出”?通过发送信号还是通过其他方式?
我知道Vim交换文件,并且可以杀死Vim并从交换中恢复。我问是否有一种“清洁”的方式。
假设我有一个挂起的X11服务器,这使我无法保存X11服务器控制的XTerm Vim会话中的工作。(不是GVim,只是常规的Vim-in-XTerm。)
有没有办法(从另一个终端)告诉正在运行的Vim进程从命令行“全部保存并退出”?通过发送信号还是通过其他方式?
我知道Vim交换文件,并且可以杀死Vim并从交换中恢复。我问是否有一种“清洁”的方式。
Answers:
最近遇到了这个问题(通过另一种方式:Vim在远程服务器上运行,而我忘记了屏幕),我决定寻找一种方法。
第一个想法是查找Vim使用的文件描述符并尝试对其进行写入。Vim的fds指向由终端仿真器打开的psedoterminal,这很自然:
$ ls -l /proc/$(pgrep -n vim)/fd/
total 0
lrwx------ 1 muru muru 64 Nov 17 01:25 0 -> /dev/pts/14
lrwx------ 1 muru muru 64 Nov 17 01:25 1 -> /dev/pts/14
lrwx------ 1 muru muru 64 Nov 17 01:25 2 -> /dev/pts/14
lrwx------ 1 muru muru 64 Nov 17 01:25 3 -> socket:[99564312]
但是,最初的几次尝试都失败了:
echo '^[:wq^M' > /proc/$(pgrep -n vim)/fd/0
echo ':wq^M' > /proc/$(pgrep -n vim)/fd/0
echo ':wq^M' > /proc/$(pgrep -n vim)/fd/0
echo '^C' > /proc/$(pgrep -n vim)/fd/0
printf "%s" '^[:wqa!^M' > /proc/$(pgrep -n vim)/fd/0
的^[
和^M
通过获得CtrlVEsc和CtrlVEnter分别。
它们都会导致字符显示在终端上(在将其应用于远程会话之前,我正在本地进行测试)。谷歌搜索,我发现了这篇SO帖子,使用Python编写了伪终端设备:
#!/usr/bin/python
import sys,os,fcntl,termios
if len(sys.argv) != 3:
sys.stderr.write("usage: ttyexec.py tty command\n")
sys.exit(1)
fd = os.open("/dev/" + sys.argv[1], os.O_RDWR)
cmd=sys.argv[2]
for i in range(len(cmd)):
fcntl.ioctl(fd, termios.TIOCSTI, cmd[i])
fcntl.ioctl(fd, termios.TIOCSTI, '\n')
os.close(fd)
并在交互式python shell上进行尝试:
$ sudo python3
Python 3.5.0 (default, Sep 20 2015, 11:28:25)
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os, fcntl, termios
>>> fd = os.open('/dev/pts/14', os.O_RDWR)
>>> a = '\033:wqa!\n'
>>> for i in a: fcntl.ioctl(fd, termios.TIOCSTI, i);
...
b'\x1b'
b':'
b'w'
b'q'
b'a'
b'!'
b'\n'
>>>
做完了!
您可以在运行时将命令发送到外部vim ...
例如,执行以下操作:
vim --servername vim
将导致vim启动名称为“ vim”的服务器。调用两次,新服务器将被命名为“ vim1”,三次将被命名为“ vim2”,依此类推。您可能想对该命令做一个别名。
通过查看窗口标题,您可以知道特定实例命名的服务器。当你看到:
[无名称] +-VIM3
服务器名称不区分大小写“ VIM3”(“ vim3”将指向同一实例。)。请注意,如果您看到:
[无名称] +-VIM
这并不一定意味着它有一个名为“ VIM”的服务器。您可以通过列出服务器名称来确保服务器存在:
vim --serverlist
但是,仅对于“ VIM”才出现问题。如果您看到“ GVIM”或其他名称后加数字,则表示它是服务器。
现在,对您的问题,您可以保存所有内容并通过执行以下操作退出给定的vim实例:
vim --servername vim2 --remote-send $'\e:wqa\n'
如果您处于插入或命令模式,我们使用转义键返回正常模式。您可以执行除之外的其他操作:wqa
,但这对我来说似乎是最合适的,因为它将留下无法保存的缓冲区交换文件(因为它们是新文件并且没有文件名等)。
如果要对所有实例(例如此处的情况)都这样做,则可以像这样遍历服务器列表:
for instance in $(vim --serverlist); do
vim --servername $instance --remote-send $'\e:wqa\n'
done
如果由于某种原因您不喜欢--remote-send
,则可以改用--remote-expr
which,它的优点是它将导致客户端输出结果或可能导致的错误,例如:
$ vim --servername vim2 --remote-expr 'execute("wqa")'
E141: No file name for buffer 1
注意,使用Vim的服务器功能要求Vim是使用该+clientserver
选项构建的。
reptyr
使用系统的程序包管理器安装命令,例如:
sudo apt install reptyr
pacman -Sy reptyr
然后使用reptyr
命令将远程tty切换到本地(新)tty,如下所示:
ssh user@remote-hostname
ps auxw | grep -i vim
reptyr PID
命令输出中PID
的进程ID 在哪里ps
。
在错误:
无法附加到pid 12345:权限被拒绝
将“ ptrace范围”更改为0:
sudo su -
echo 0 > /proc/sys/kernel/yama/ptrace_scope
一旦vim会话从旧会话切换到新会话,保存并照常退出。请注意,您可能必须按Enter刷新控制台。
如果您优雅地杀死Vim怎么办?
kill -s 15 -p [PID for Vim]
kill -s(信号)15称为SIGTERM,它告诉该进程正常关闭自身。
要获取Vim的PID(进程ID),请使用:
ps ax | grep vim