Jak zamknąć Vima zewnętrznie?

23

Powiedzmy, że mam zawieszony serwer X11, co uniemożliwia mi zapisanie pracy z sesji XTerm Vim, którą kontroluje serwer X11. (Nie GVim, tylko zwykły Vim-in-XTerm.)

Czy jest sposób, w jaki mógłbym (z innego terminala) powiedzieć działającemu procesowi Vima, aby „zapisz wszystko i wyjdź” z linii poleceń? Wysyłając sygnał lub w inny sposób?

Wiem o plikach wymiany Vima i że mogę po prostu zabić Vima i odzyskać go po zamianie. Pytam, czy istnieje „czystszy” sposób.

DevSolar
źródło
6
Jeśli te sesje Vima zostały uruchomione z włączonym serwerem (tak jak domyślnie robi to gvim), możesz użyć do tego funkcji klienta i serwera Vima. Inną opcją może być użycie reptyr do zmuszenia programów Vima do nowego terminala, powiedzmy TTY, a następnie zamknij je.
muru

Odpowiedzi:

25

Po niedawnym napotkaniu tego problemu (w inny sposób: Vim działający na zdalnym serwerze, a zapomniałem ekranu), postanowiłem polować na jakiś sposób.

Pierwszym pomysłem było sprawdzenie deskryptorów plików używanych przez Vima i próba napisania do niego. Fds Vima wskazują na psedoterminal otwarty przez emulator terminala, co naturalnie:

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

Jednak moje pierwsze próby nie powiodły się:

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

^[I ^Motrzymano z CtrlVEsci CtrlVEnter, odpowiednio.

Wszystkie spowodowały pojawienie się znaków na terminalu (testowałem to lokalnie, przed zastosowaniem go do sesji zdalnej). Googlując się, znalazłem ten post SO , używając Pythona do pisania na urządzeniu pseudoterminalowym:

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

I wypróbowanie go na interaktywnej powłoce pytona działało:

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

Gotowy!

muru
źródło
1
Zauważ, że skrypt Pythona wymaga uprawnień roota, aby uzyskać dostęp do terminala.
martinkunev
6

Możesz wysyłać polecenia do vima zewnętrznie, jeśli używasz ...

Serwery Vim

Na przykład robienie:

vim --servername vim

spowoduje, że vim uruchomi serwer o nazwie „vim”. Wywołaj go dwa razy, a nowy serwer zostanie nazwany „vim1”, nazwij go trzykrotnie, a będzie to „vim2” itp. Możesz utworzyć alias dla tego polecenia.

Możesz wiedzieć, jaki serwer nazywa się określona instancja, patrząc na tytuł okna. Kiedy widzisz:

[Bez nazwy] + - VIM3

w nazwie serwera rozróżniana jest wielkość liter „VIM3” („vim3” odnosi się do tej samej instancji). Pamiętaj, że jeśli zobaczysz:

[Bez nazwy] + - VIM

to niekoniecznie oznacza, że ​​ma serwer o nazwie „VIM”. Możesz upewnić się, że serwer istnieje, wymieniając nazwy serwerów z:

vim --serverlist

Jednak pytanie dotyczy tylko „VIM”. Jeśli widzisz „GVIM” lub inną nazwę z dołączonym numerem, oznacza to, że jest to serwer.

Jak korzystać z klienta

Teraz, na swoje pytanie, możesz zapisać wszystko i zamknąć daną instancję vima, wykonując np .:

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

Używamy klawisza Escape, aby powrócić do trybu normalnego na wypadek, gdybyś był w trybie wstawiania lub poleceń. Możesz zrobić coś innego :wqa, ale wydaje mi się to najbardziej odpowiednie, ponieważ pozostawi pliki wymiany buforów, których nie można zapisać (ponieważ są nowe i nie mają nazwy pliku itp.).

Jeśli chcesz to zrobić dla wszystkich instancji, takich jak w twoim przypadku, możesz po prostu przeglądać listę serwerów w następujący sposób:

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

Jeśli z jakiegoś powodu ci się nie podoba --remote-send, możesz zamiast tego użyć tego, --remote-exprco ma tę zaletę, że spowoduje wyświetlenie przez klienta wyniku lub błędu, który mógł spowodować, na przykład:

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

E141: No file name for buffer 1

Zauważ, że korzystanie z funkcjonalności serwera Vima wymaga, aby Vim został zbudowany z tą +clientserveropcją.

JoL
źródło
5

Zainstaluj reptyrpolecenie za pomocą menedżera pakietów systemu, takiego jak:

sudo apt install reptyr
pacman -Sy reptyr

Następnie użyj reptyrpolecenia, aby przełączyć zdalny tty na lokalny (nowy) tty w następujący sposób:

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

Gdzie PIDjest identyfikator procesu z danych pswyjściowych polecenia.

Po błędzie:

Nie można dołączyć do pid 12345: Odmowa dostępu

Zmień „zakres ptrace” na 0:

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

Po zmianie sesji vim ze starej na nową, zapisz i wyjdź jak zwykle. Pamiętaj, że może być konieczne naciśnięcie przycisku, Enteraby odświeżyć konsolę.

Dave Jarvis
źródło
0

Co jeśli z wdziękiem zabijesz Vima?

kill -s 15 -p [PID for Vim]

kill -s (sygnał) 15 nazywa się SIGTERM, co oznacza, że ​​proces ten z wdziękiem się wyłącza.

Aby uzyskać PID (identyfikator procesu) Vima, użyj:
ps ax | grep vim

Gustav Blomqvist
źródło
1
Vim nie zapisuje automatycznie niezapisanych zmian, bez względu na to, które sygnały zostały wysłane.
muru