ฉันจะปิด Vim ภายนอกได้อย่างไร


23

สมมติว่าฉันมีเซิร์ฟเวอร์ X11 ที่หยุดทำงานทำให้ฉันไม่สามารถบันทึกงานจากเซสชัน XTerm Vim ที่เซิร์ฟเวอร์ X11 ควบคุม (ไม่ใช่ GVim เพียงปกติ Vim-in-XTerm)

มีวิธีที่ฉันสามารถ (จาก terminal ที่แตกต่างกัน) บอกกระบวนการเรียกใช้เป็นกลุ่มเพื่อ "บันทึกทั้งหมด & ออก" จากบรรทัดคำสั่งหรือไม่ โดยการส่งสัญญาณหรือผ่านวิธีอื่น?

ฉันรู้เกี่ยวกับไฟล์ swap ของ Vim และฉันก็สามารถฆ่า Vim และกู้คืนจาก swap ได้ ฉันกำลังถามว่ามีวิธี "สะอาดกว่า" หรือไม่


6
หากเซสชัน Vim เหล่านั้นเริ่มต้นด้วยการเปิดใช้งานเซิร์ฟเวอร์ (เช่นเดียวกับที่ gvim ทำตามค่าเริ่มต้น) คุณสามารถใช้ฟังก์ชันไคลเอนต์ - เซิร์ฟเวอร์ของ Vim ได้ อีกทางเลือกหนึ่งอาจจะใช้ reptyr เพื่อบังคับให้โปรแกรม Vim ไปที่เทอร์มินัลใหม่พูดถึง TTY และปิดมันแล้ว
muru

คำตอบ:


25

เมื่อเร็ว ๆ นี้พบปัญหานี้ (ผ่านวิธีอื่น: เรียกใช้บนเซิร์ฟเวอร์ระยะไกลและฉันลืมหน้าจอ) ฉันตัดสินใจที่จะหาวิธี

แนวคิดแรกคือค้นหาตัวอธิบายไฟล์ที่ Vim ใช้และลองเขียนลงไป fds ของ Vim ชี้ไปที่ psedoterminal ที่เปิดโดย terminal emulator โดยธรรมชาติเพียงพอ:

$ 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 เขียนไปยังอุปกรณ์ 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)

และลองใช้กับ python shell ที่ใช้การได้:

$ 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
โปรดทราบว่าสคริปต์ไพ ธ อนต้องการสิทธิ์รูทเพื่อเข้าถึงเทอร์มินัล
martinkunev

6

คุณสามารถส่งคำสั่งไปยังกลุ่มภายนอกถ้าคุณใช้ ...

เซิร์ฟเวอร์ที่เป็นกลุ่ม

ตัวอย่างเช่นการทำ:

vim --servername vim

จะทำให้กลุ่มเรียกใช้เซิร์ฟเวอร์ที่มีชื่อ "vim" เรียกมันสองครั้งและเซิร์ฟเวอร์ใหม่จะถูกเรียกว่า "vim1" เรียกว่าสามครั้งและจะเป็น "vim2" เป็นต้นคุณอาจต้องการสร้างนามแฝงของคำสั่งนั้น

คุณสามารถทราบได้ว่าเซิร์ฟเวอร์ชื่ออินสแตนซ์ใดโดยดูจากชื่อหน้าต่าง เมื่อคุณเห็น:

[ไม่มีชื่อ] + - VIM3

ชื่อเซิร์ฟเวอร์จะต้องตรงตามตัวพิมพ์ใหญ่ - เล็ก "VIM3" ("vim3" จะอ้างอิงกับอินสแตนซ์เดียวกัน) โปรดทราบว่าถ้าคุณเห็น:

[ไม่มีชื่อ] + - VIM

ไม่ได้แปลว่ามีเซิร์ฟเวอร์ชื่อ "VIM" คุณสามารถตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์นั้นมีอยู่โดยการแสดงรายชื่อเซิร์ฟเวอร์ด้วย:

vim --serverlist

ถึงกระนั้นคำถามก็เกิดขึ้นสำหรับ "VIM" โดยเฉพาะ หากคุณเห็น "GVIM" หรือชื่ออื่นที่มีหมายเลขต่อท้ายอยู่นั่นหมายความว่าเป็นเซิร์ฟเวอร์

วิธีใช้งานไคลเอนต์

ตอนนี้คำถามของคุณคุณสามารถบันทึกทั้งหมดและออกจากอินสแตนซ์เสียงเรียกเข้าที่กำหนดโดยทำเช่น:

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

เราใช้การหลบหลีกเพื่อกลับสู่โหมดปกติในกรณีที่คุณอยู่ในโหมดแทรกหรือโหมดคำสั่ง คุณสามารถทำสิ่งอื่นนอกเหนือจาก:wqaนี้ได้ แต่นั่นดูเหมือนจะเหมาะสมที่สุดสำหรับฉันเพราะมันจะปล่อยให้ swapfiles ของบัฟเฟอร์ที่ไม่สามารถบันทึกได้ (เพราะมันใหม่และไม่มีชื่อไฟล์ ฯลฯ )

หากคุณต้องการทำเช่นนั้นสำหรับอินสแตนซ์ทั้งหมดในกรณีของคุณที่นี่คุณสามารถวนรอบรายการเซิร์ฟเวอร์ดังนี้:

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

PIDID กระบวนการอยู่ที่ไหนจาก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 (ID กระบวนการ) ของการใช้ Vim:
ps ax | grep vim


1
Vim ไม่ได้เขียนการเปลี่ยนแปลงที่ไม่ได้บันทึกโดยอัตโนมัติไม่ว่าจะส่งสัญญาณใด
muru
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.