Как мне закрыть Vim снаружи?

23

Допустим, у меня есть сервер X11, который зависает, и я не могу сохранить работу из сеанса XTerm Vim, которым управляет сервер X11. (Не GVim, просто обычный Vim-in-XTerm.)

Есть ли способ, которым я мог бы (с другого терминала) сказать запущенному процессу Vim «сохранить все и выйти» из командной строки? Отправив сигнал или каким-либо другим способом?

Я знаю о файлах подкачки Vim, и что я могу просто убить Vim и восстановиться после свопинга. Я спрашиваю, есть ли "более чистый" способ.

DevSolar
источник
6
Если эти сеансы Vim были запущены с включенным сервером (как это делает gvim по умолчанию), вы можете использовать для этого функциональность клиент-сервер Vim. Другим вариантом может быть использование reptyr для принудительной установки программ Vim на новый терминал, скажем, TTY, и затем их закрытие.
Муру

Ответы:

25

Недавно столкнувшись с этой проблемой (другим способом: Vim работает на удаленном сервере, и я забыл экран), я решил найти способ.

Первой идеей было поиск файловых дескрипторов, используемых Vim, и попытка записи в него. Fds Vim указывает на пседотерминал, открытый эмулятором терминала, естественно:

$ 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, соответственно.

Все они привели к появлению символов на терминале (я проверял это локально, прежде чем применить его к удаленному сеансу). Погуглив, я нашел этот пост , использующий 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 сработало:

$ 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'
>>> 

Выполнено!

Мур
источник
1
Обратите внимание, что скрипту python требуются права root для доступа к терминалу.
Мартынкунев
6

Вы можете отправлять команды vim извне, если вы работаете ...

Vim серверы

Например, делать:

vim --servername vim

заставит vim запустить сервер с именем "vim". Назовите его дважды, и новый сервер будет называться «vim1», трижды назовите его, это будет «vim2» и т. Д. Возможно, вы захотите создать псевдоним этой команды.

Вы можете узнать, на каком сервере назван конкретный экземпляр, посмотрев на заголовок окна. Когда ты видишь:

[Без имени] + - VIM3

имя сервера нечувствительно к регистру "VIM3" ("vim3" будет ссылаться на тот же экземпляр). Обратите внимание, что если вы видите:

[Без имени] + - VIM

это не обязательно означает, что у него есть сервер с именем "VIM". Вы можете убедиться, что сервер существует, перечислив имена серверов с помощью:

vim --serverlist

Тем не менее, вопрос возникает только для «ВИМ», в частности. Если вы видите «GVIM» или какое-то другое имя с добавленным к нему номером, это означает, что это сервер.

Как пользоваться клиентом

Теперь, на ваш вопрос, вы можете сохранить все и выйти из данного экземпляра vim, выполнив, например:

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

Мы используем escape для возврата в нормальный режим, если вы находитесь в режиме вставки или командном режиме. Вы можете сделать что-то кроме :wqa, но это кажется мне наиболее подходящим, потому что это оставит файлы обмена буферов, которые не могут быть сохранены (потому что они новые и не имеют имени файла и т. Д.).

Если вы хотите сделать это для всех экземпляров, как в вашем случае здесь, вы можете просто перебрать список серверов следующим образом:

for instance in $(vim --serverlist); do
  vim --servername $instance --remote-send $'\e:wqa\n'
done

Если по какой-то причине вам не нравится --remote-send, вы можете использовать вместо --remote-exprэтого то преимущество, которое заставит клиента выводить результат или ошибку, которую он мог вызвать, например, так:

$ vim --servername vim2 --remote-expr 'execute("wqa")'

E141: No file name for buffer 1

Обратите внимание, что использование функциональности сервера Vim требует, чтобы Vim был собран с +clientserverопцией.

Йол
источник
5

Установите reptyrкоманду с помощью системного менеджера пакетов, такого как:

sudo apt install reptyr
pacman -Sy reptyr

Затем используйте reptyrкоманду для переключения удаленного tty на локальный (новый) tty следующим образом:

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

Где PIDнаходится идентификатор процесса из psвывода команды.

После ошибки:

Невозможно прикрепить к pid 12345: в доступе отказано

Измените «ptrace scope» на 0:

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

Как только сессия vim будет перенесена из старого в новый, сохраните и закройте как обычно. Обратите внимание, что вам, возможно, придется нажать, Enterчтобы обновить консоль.

Дейв Джарвис
источник
0

Что если ты изящно убьешь Вима?

kill -s 15 -p [PID for Vim]

kill -s (сигнал) 15 называется SIGTERM, который говорит, что процесс корректно завершает свою работу.

Чтобы получить PID (идентификатор процесса) Vim, используйте:
ps ax | grep vim

Густав Бломквист
источник
1
Vim не записывает несохраненные изменения автоматически, независимо от того, какие сигналы были отправлены.
Муру