Bagaimana saya menutup Vim secara eksternal?

23

Katakanlah saya memiliki server X11 yang menggantung, membuat saya tidak menyimpan pekerjaan dari sesi XTerm Vim yang dikendalikan server X11. (Bukan GVim, hanya Vim-in-XTerm biasa.)

Apakah ada cara yang saya bisa (dari terminal yang berbeda) memberitahu proses Vim yang sedang berjalan untuk "menyimpan semua & keluar" dari baris perintah? Dengan mengirimkan sinyal, atau melalui cara lain?

Saya tahu tentang file swap Vim, dan saya hanya bisa membunuh Vim dan pulih dari swap. Saya bertanya apakah ada cara "bersih".

DevSolar
sumber
6
Jika sesi-sesi Vim itu dimulai dengan server yang diaktifkan (seperti gvim tidak secara default), maka Anda dapat menggunakan fungsionalitas server-klien Vim untuk melakukannya. Opsi lain mungkin menggunakan reptyr untuk memaksa program Vim ke terminal baru, katakan TTYs, dan kemudian tutup.
muru

Jawaban:

25

Baru-baru ini mengalami masalah ini (melalui cara lain: Vim berjalan pada server jauh, dan saya lupa layar), saya memutuskan untuk mencari cara.

Gagasan pertama adalah mencari deskriptor file yang digunakan oleh Vim dan mencoba menulisnya. Fds Vim menunjuk ke psedoterminal yang dibuka oleh terminal emulator, cukup alami:

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

Namun, beberapa upaya awal saya gagal:

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

Itu ^[dan ^Mdiperoleh oleh CtrlVEscdan CtrlVEnter, masing-masing.

Mereka semua menghasilkan karakter yang muncul di terminal (saya menguji ini secara lokal, sebelum menerapkannya pada sesi jarak jauh). Googling sekitar, saya menemukan posting SO ini , menggunakan Python untuk menulis ke perangkat pseudoterminal:

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

Dan mencobanya pada shell python interaktif berhasil:

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

Selesai!

muru
sumber
1
Perhatikan bahwa skrip python memerlukan izin root untuk mengakses terminal.
martinkunev
6

Anda dapat mengirim perintah ke vim secara eksternal jika Anda menjalankan ...

Server Vim

Misalnya, melakukan:

vim --servername vim

akan menyebabkan vim meluncurkan server dengan nama "vim". Sebut dua kali dan server baru akan disebut "vim1", sebut saja tiga kali dan itu akan menjadi "vim2", dll. Anda mungkin ingin membuat alias dari perintah itu.

Anda bisa tahu server apa yang dinamai instance tertentu dengan melihat judul jendela. Ketika kamu melihat:

[Tanpa Nama] + - VIM3

nama server bersifat case-insensitive "VIM3" ("vim3" akan merujuk pada contoh yang sama.). Perhatikan bahwa jika Anda melihat:

[Tanpa Nama] + - VIM

itu tidak selalu berarti bahwa itu punya server bernama "VIM". Anda dapat memastikan server ada dengan mendaftarkan nama server dengan:

vim --serverlist

Namun, pertanyaannya hanya muncul untuk "VIM", khususnya. Jika Anda melihat "GVIM" atau nama lain dengan nomor yang ditambahkan, maka itu berarti server.

Bagaimana cara menggunakan klien

Sekarang, ke pertanyaan Anda, Anda dapat menyimpan semua dan keluar dari contoh vim tertentu dengan melakukan, misalnya:

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

Kami menggunakan pelarian untuk kembali ke mode normal jika Anda berada dalam mode insert atau command. Anda dapat melakukan sesuatu selain dari itu :wqa, tetapi itu tampaknya yang paling tepat bagi saya karena itu akan meninggalkan swapfiles buffer yang tidak dapat diselamatkan (karena mereka baru dan tidak memiliki nama file, dll.).

Jika Anda ingin melakukannya untuk semua contoh seperti dalam kasus Anda di sini, Anda bisa mengulang daftar server seperti:

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

Jika karena alasan tertentu Anda tidak suka --remote-send, Anda dapat menggunakan --remote-expryang memiliki kelebihan yang akan menyebabkan klien untuk menampilkan hasil atau kesalahan yang mungkin disebabkan, seperti:

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

E141: No file name for buffer 1

Perhatikan bahwa menggunakan fungsionalitas server Vim mengharuskan Vim dibangun dengan +clientserveropsi.

JoL
sumber
5

Instal reptyrperintah menggunakan manajer paket sistem, seperti:

sudo apt install reptyr
pacman -Sy reptyr

Kemudian gunakan reptyrperintah untuk mengalihkan tty jarak jauh ke tty lokal (baru), sebagai berikut:

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

Di mana PIDID proses dari psoutput perintah.

Setelah kesalahan:

Tidak dapat melampirkan pid 12345: Izin ditolak

Ubah "ruang lingkup ptrace" ke 0:

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

Setelah sesi vim telah ditukar dari sesi lama ke yang baru, simpan dan keluar seperti biasa. Perhatikan bahwa Anda mungkin harus menekan Enteruntuk menyegarkan konsol.

Dave Jarvis
sumber
0

Bagaimana jika Anda dengan anggun membunuh Vim?

kill -s 15 -p [PID for Vim]

kill -s (signal) 15 disebut SIGTERM yang memberitahukan proses itu untuk mematikan dirinya dengan anggun.

Untuk mendapatkan PID (ID Proses) Vim, gunakan:
ps ax | grep vim

Gustav Blomqvist
sumber
1
Vim tidak secara otomatis menulis perubahan yang belum disimpan, tidak peduli sinyal mana yang dikirim.
muru