Як закрити Vim зовні?


23

Скажімо, у мене висить сервер X11, який не дозволяє врятувати роботу від сеансу XTerm Vim, яким керує сервер X11. (Не GVim, просто звичайний Vim-in-XTerm.)

Чи є спосіб, щоб я міг (з іншого терміналу) сказати запущеному процесу Vim, щоб "зберегти всі та вийти" з командного рядка? Посилаючи сигнал, або через якісь інші засоби?

Я знаю про файли swap Vim, і що я міг би просто вбити Vim і відновити його. Я запитую, чи існує "чистіший" спосіб.


6
Якщо ці сеанси Vim були запущені з увімкненим сервером (як, наприклад, гвім за замовчуванням), ви можете використовувати для цього функціонал клієнта-сервера Vim. Іншим варіантом може бути використання рептика для примушування програм 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.

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

І випробувати його на інтерактивній оболонці пітона спрацювало:

$ 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 вимагає кореневих дозволів для доступу до терміналу.
мартінкунев

6

Ви можете надсилати команди vim зовні, якщо ви працюєте ...

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що має перевагу в тому, що клієнт виведе результат або помилку, яку він може спричинити, наприклад:

$ 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" на 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 не записує автоматично збережені зміни, незалежно від того, які сигнали були надіслані.
муру
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.