假设我有一个挂起的X11服务器,这使我无法保存X11服务器控制的XTerm Vim会话中的工作。 (不是GVim,只是常规的Vim-in-XTerm。)

是否可以(从另一个终端)告诉正在运行的Vim进程从命令行“保存并退出” ?通过发送信号或通过其他方式?

我了解Vim交换文件,因此我可以杀死Vim并从交换中恢复。我问是否有一种“清洁”的方式。

评论

如果这些Vim会话是在启用服务器的情况下启动的(例如gvim会默认启用),则可以使用Vim的客户端-服务器功能来实现。另一种选择可能是使用reptyr将Vim程序强制到一个新的终端(例如TTY),然后关闭它们。

#1 楼

最近遇到了这个问题(通过另一种方式: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。

它们都导致字符显示在终端上(在将其应用于远程会话之前,我正在本地进行测试)。在Google上四处搜寻,我发现了这篇SO帖子,使用Python编写了伪终端设备:
/>并在交互式python shell上进行尝试:

 #!/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)
 


完成! br />

评论


请注意,python脚本需要root权限才能访问终端。

–martinkunev
18年8月16日在22:44

#2 楼

如果您正在运行,则可以从外部向vim发送命令...

Vim服务器

例如,执行以下操作:

vim --servername vim


将使vim启动名称为“ vim”的服务器。调用两次,新服务器将被命名为“ vim1”,三次将被命名为“ vim2”,依此类推。您可能想对该命令进行别名。服务器通过查看窗口标题来命名特定实例。当您看到:


[无名称] +-VIM3


服务器名称不区分大小写“ VIM3”(“ vim3”指同一实例。)。请注意,如果您看到:


[无名称] +-VIM


,这不一定意味着它有一个名为“ VIM”。您可以通过列出服务器名称来确保服务器存在:

vim --serverlist


不过,问题仅在“ VIM”出现。如果您看到“ GVIM”或其他名称后加数字,则表示它是服务器。

如何使用客户端

现在,您的问题,您可以保存所有内容并通过执行以下操作退出给定的vim实例,例如:命令模式。您可以执行:wqa以外的其他操作,但是这对我来说似乎最合适,因为它将留下无法保存的缓冲区交换文件(因为它们是新文件并且没有文件名等)。

如果要针对所有实例(例如此处的情况)这样做,则可以像这样遍历服务器列表:

vim --servername vim2 --remote-send $'\e:wqa\n'


由于某些原因,您不喜欢--remote-send,可以改用--remote-expr,其优点是将导致客户端输出结果或可能导致的错误,例如: br />

请注意,使用Vim的服务器功能要求Vim是使用+clientserver选项构建的。

#3 楼

使用系统的软件包管理器安装reptyr命令,例如:

sudo apt install reptyr
pacman -Sy reptyr


然后使用reptyr命令将远程tty切换到本地(新)tty,如下所示:

ssh user@remote-hostname
ps auxw | grep -i vim
reptyr PID


其中PID是来自ps命令输出的进程ID。

出现错误:


无法附加到pid 12345:权限被拒绝


将“ ptrace范围”更改为0:

sudo su -
echo 0 > /proc/sys/kernel/yama/ptrace_scope


vim会话从旧会话切换到新会话后,照常保存并退出。请注意,您可能必须按Enter刷新控制台。

评论


reptyr绝对是进行此类工作的最佳工具...就像Linux的柱塞一样:D

– Avio
20-2-20在14:27

#4 楼

如果您优雅地杀死Vim怎么办?

kill -s 15 -p [PID for Vim]

kill -s(信号)15被称为SIGTERM,它告诉该进程正常关闭自身。 Vim使用的进程ID):ps ax | grep vim

评论


无论发送哪个信号,Vim都不会自动写入未保存的更改。

–muru
17年9月15日在5:22

#5 楼

我不知道它是否能正常工作,但是您可以尝试安装一个自动听SigUSR1的命令:

augroup save_and_exit | au!
    au SigUSR1 * wall | qa!
augroup END


然后,从另一个外壳中,您可以运行:

$ kill -s USR1 1234


其中1234是Vim进程的pid。在Vim中,可以通过getpid()函数获得后者。

这需要补丁8.2.0952来获取SigUSR1事件。