Comment fermer Vim en externe?

23

Disons que j'ai un serveur X11 suspendu, ce qui m'empêche d'enregistrer le travail de la session XTerm Vim que le serveur X11 contrôle. (Pas GVim, juste Vim-in-XTerm régulier.)

Existe-t-il un moyen (à partir d'un autre terminal) de dire au processus Vim en cours de «tout enregistrer et quitter» à partir de la ligne de commande? En envoyant un signal, ou par d'autres moyens?

Je connais les fichiers d'échange de Vim et je pourrais simplement tuer Vim et récupérer à partir de l'échange. Je demande s'il existe une méthode "plus propre".

DevSolar
la source
6
Si ces sessions Vim ont été démarrées avec un serveur activé (comme gvim le fait par défaut), vous pouvez utiliser la fonctionnalité client-serveur de Vim pour le faire. Une autre option pourrait être d'utiliser reptyr pour forcer les programmes Vim vers un nouveau terminal, par exemple les ATS, puis les fermer.
muru

Réponses:

25

Ayant récemment rencontré ce problème (via une autre façon: Vim fonctionnant sur un serveur distant, et j'avais oublié l'écran), j'ai décidé de chercher un moyen.

La première idée a été de rechercher les descripteurs de fichiers utilisés par Vim et d'essayer d'y écrire. Les fds de Vim pointent vers le psedoterminal ouvert par l'émulateur de terminal, assez naturellement:

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

Cependant, mes quelques premières tentatives ont échoué:

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

Le ^[et ^Mont été obtenus respectivement par CtrlVEscet CtrlVEnter.

Ils ont tous fait apparaître les personnages sur le terminal (je testais cela localement, avant de l'appliquer à la session distante). Googler autour, j'ai trouvé ce message SO , en utilisant Python pour écrire sur le périphérique 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)

Et l'essayer sur un shell python interactif a fonctionné:

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

Terminé!

muru
la source
1
Notez que le script python nécessite des autorisations root pour accéder au terminal.
martinkunev
6

Vous pouvez envoyer des commandes à vim en externe si vous exécutez ...

Serveurs Vim

Par exemple, faire:

vim --servername vim

fera en sorte que vim lance un serveur avec le nom "vim". Appelez-le deux fois et le nouveau serveur sera appelé "vim1", appelez-le trois fois et ce sera "vim2", etc. Vous voudrez peut-être créer un alias de cette commande.

Vous pouvez savoir quel serveur une instance particulière est nommée en regardant le titre de la fenêtre. Quand tu vois:

[Sans nom] + - VIM3

le nom du serveur est insensible à la casse "VIM3" ("vim3" ferait référence à la même instance.). Notez que si vous voyez:

[Sans nom] + - VIM

cela ne signifie pas nécessairement qu'il a un serveur nommé "VIM". Vous pouvez vous assurer que le serveur existe en répertoriant les noms de serveur avec:

vim --serverlist

Pourtant, la question ne se pose que pour "VIM", en particulier. Si vous voyez "GVIM" ou un autre nom avec un numéro ajouté, cela signifie que c'est un serveur.

Comment utiliser le client

Maintenant, sur votre question, vous pouvez tout sauvegarder et quitter une instance de vim donnée en faisant, par exemple:

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

Nous utilisons l'évasion pour revenir au mode normal au cas où vous êtes en mode insertion ou commande. Vous pouvez faire autre chose que :wqa, mais cela me semble le plus approprié car cela laissera les fichiers d'échange de tampons qui n'ont pas pu être enregistrés (car ils sont nouveaux et n'ont pas de nom de fichier, etc.).

Si vous voulez le faire pour toutes les instances comme dans votre cas ici, vous pouvez simplement parcourir la liste des serveurs comme suit:

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

Si pour une raison que vous n'aimez pas --remote-send, vous pouvez utiliser à la place --remote-exprce qui a l'avantage de faire en sorte que le client génère le résultat ou l'erreur qu'il a pu causer, comme ceci:

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

E141: No file name for buffer 1

Notez que l'utilisation des fonctionnalités du serveur de Vim nécessite que Vim ait été construit avec l' +clientserveroption.

JoL
la source
5

Installez la reptyrcommande à l'aide du gestionnaire de packages du système, tel que:

sudo apt install reptyr
pacman -Sy reptyr

Utilisez ensuite la reptyrcommande pour basculer le terminal distant vers le terminal local (nouveau), comme suit:

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

PIDest l'ID de processus de la pssortie de la commande.

Sur l'erreur:

Impossible de joindre au pid 12345: autorisation refusée

Remplacez la "portée ptrace" par 0:

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

Une fois que la session vim a été échangée de l'ancienne session à la nouvelle, enregistrez et quittez comme d'habitude. Notez que vous devrez peut-être appuyer sur Enterpour actualiser la console.

Dave Jarvis
la source
0

Et si tu tuais Vim avec grâce?

kill -s 15 -p [PID for Vim]

kill -s (signal) 15 est appelé SIGTERM, ce qui indique à ce processus de s'arrêter normalement.

Pour obtenir le PID (Process ID) de Vim, utilisez:
ps ax | grep vim

Gustav Blomqvist
la source
1
Vim n'écrit pas automatiquement les modifications non enregistrées, quels que soient les signaux envoyés.
muru