Vimを外部で閉じるにはどうすればよいですか?


23

ハングしているX11サーバーがあり、X11サーバーが制御するXTerm Vimセッションの作業を保存できないようにするとします。(GVimではなく、通常のVim-in-XTermのみ。)

(別の端末から)実行中のVimプロセスにコマンドラインから「すべて保存して終了」するように指示する方法はありますか?信号を送信することにより、または他の手段を通じて?

Vimスワップファイルについて知っています。Vimを強制終了して、スワップから回復することができます。「もっときれいな」方法があるかどうかを尋ねています。


6
これらのVimセッションがサーバーを有効にして開始された場合(gvimがデフォルトで行うように)、Vimのクライアントサーバー機能を使用してこれを行うことができます。別のオプションは、reptyrを使用して、Vimプログラムを新しい端末(TTYなど)に強制し、それらを閉じることです。
ムル

回答:


25

最近この問題に遭遇したので(別の方法:リモートサーバーで実行しているVimで、画面を忘れてしまいました)、方法を探すことにしました。

最初のアイデアは、Vimが使用するファイル記述子を検索し、書き込みを試みることでした。Vimのfdsは、ターミナルエミュレータによって開かれたpsedoterminalを指します。

$ 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

得られたとそれぞれ。^[^MCtrlVEscCtrlVEnter

それらはすべて、文字を端末に表示しました(リモートセッションに適用する前に、これをローカルでテストしていました)。グーグルで、私はこの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)

そして、インタラクティブな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スクリプトには、ターミナルにアクセスするためのルート権限が必要であることに注意してください。
-martinkunev

6

実行している場合は、外部からvimにコマンドを送信できます...

Vimサーバー

たとえば、次のことを行います。

vim --servername vim

vimは「vim」という名前のサーバーを起動します。2回呼び出すと、新しいサーバーは「vim1」と呼ばれ、3回呼び出すと「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プロセスIDはどこにありpsますか。

エラー発生時:

pid 12345に接続できません:許可が拒否されました

「ptrace scope」を0に変更します。

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

vimセッションが古いセッションから新しいセッションに交換されたら、通常どおり保存して終了します。Enterコンソールを更新するために押す必要がある場合があることに注意してください。


0

Vimを優雅に殺すとどうなりますか?

kill -s 15 -p [PID for Vim]

kill -s(シグナル)15はSIGTERMと呼ばれ、そのプロセスに正常にシャットダウンするよう指示します。

VimのPID(プロセスID)を取得するには:
ps ax | grep vim


1
Vimは、送信されたシグナルに関係なく、未保存の変更を自動的に書き込みません。
ムル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.