Paramiko和exec_command - 如何终止远程进程?
我正在使用Paramiko来对远程服务器上的一个文件进行tail -f
操作。
之前我们是通过ssh -t
来运行这个操作的,但这样做不太稳定,而且-t
选项还导致了我们远程调度系统的一些问题。
我想问的是,当脚本捕捉到SIGINT信号时,怎么能结束tail命令?
我的脚本是基于Python Paramiko模块中的长时间运行的ssh命令(以及如何结束它们)。
#!/usr/bin/env python2
import paramiko
import select
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -f /home/victorhooi/macbeth.txt")
while True:
try:
rl, wl, xl = select.select([channel],[],[],0.0)
if len(rl) > 0:
# Must be stdout
print channel.recv(1024)
except KeyboardInterrupt:
print("Caught control-C")
client.close()
channel.close()
exit(0)
这个脚本成功捕捉到了我的Ctrl-C操作,并且结束了。但是,它却让远程系统上的tail -f
进程继续运行。
无论是client.close()还是channel.close()都似乎无法终止它。
在异常处理块中,我可以发出什么命令来结束它呢?
远程服务器运行的是Solaris 10。
8 个回答
有一种方法可以做到这一点。它的工作方式就像在命令行中一样。
ssh -t commandname
选项 -t 是用来打开一个伪终端,这样 ssh 就能跟踪这个过程应该持续多久。通过 pormiko 也可以做到这一点,方法是
channel.get_pty()
在执行 execute_command(...) 之前。这不会像 channel.invoke_shell() 那样打开一个真正的命令行,它只是请求一个伪接口来把所有的进程连接起来。如果在远程机器上运行 ps aux 命令,你会看到这个进程现在是和 sshd 通过一个 ptxXY 接口连接在一起的。
你应该使用ssh保持连接的功能……你遇到的问题是,远程的命令行默认情况下不知道你的ssh连接已经断开。保持连接的功能可以让远程的命令行检测到你已经结束了这个连接。
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username='victorhooi', password='blahblah')
transport = client.get_transport()
transport.set_keepalive(1) # <------------------------------
# ... carry on as usual...
你可以把保持连接的时间设置得很短(甚至1秒钟)……过了几秒钟,远程的命令行就会发现ssh登录已经断开,然后它会结束由这个连接启动的任何进程。
虽然这个方法不是最有效的,但应该能奏效。在你按下 CTRL+C 后,在键盘中断处理程序里,你可以像这样执行 exec_command("killall -u %s tail" % uname)
:
#!/usr/bin/env python2
import paramiko
import select
import time
ltime = time.time()
# Or use random:
# import random
# ltime = random.randint(0, 500)
uname = "victorhooi"
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('someserver', username=uname, password='blahblah')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -%df /home/victorhooi/macbeth.txt" % ltime)
while True:
try:
rl, wl, xl = select.select([channel],[],[],0.0)
if len(rl) > 0:
# Must be stdout
print channel.recv(1024)
except KeyboardInterrupt:
print("Caught control-C")
channel.close()
try:
# open new socket and kill the proc..
client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))
except:
pass
client.close()
exit(0)
这条命令会结束所有名为 tail
的进程。不过,如果你有一些 tail
进程是你不想关闭的,这样做可能会有问题。如果是这种情况,你可以用 grep
来查找 ps
的输出,获取进程ID(pid),然后用 kill -9
来结束它。
首先,设置 tail
从文件末尾读取 n
行,然后再跟踪后面的内容。将 n
设置为一个独特的数字,比如 time.time()
,因为 tail
不在乎这个数字是否大于文件中的行数,使用 time.time()
得到的大数字不会造成问题,并且是独一无二的。接着在 ps
中用 grep
查找这个独特的数字:
client.get_transport().open_session().exec_command("kill -9 `ps -fu %s | grep 'tail -%df /home/victorhooi/macbeth.txt' | grep -v grep | awk '{print $2}'`" % (uname, ltime))