当按下 Ctrl+C 时,优雅地中止从 Windows Python Paramiko 脚本通过 SSH 执行的远程 Windows 命令
Posted
技术标签:
【中文标题】当按下 Ctrl+C 时,优雅地中止从 Windows Python Paramiko 脚本通过 SSH 执行的远程 Windows 命令【英文标题】:Gracefully abort remote Windows command executed over SSH from Windows Python Paramiko script when Ctrl+C is pressed 【发布时间】:2021-05-18 11:38:42 【问题描述】:我有一个后续问题是基于我在此处提出的问题:Run multiple commands in different SSH servers in parallel using Python Paramiko,该问题已经得到解答。
感谢上面链接的回答,我的python脚本如下:
# SSH.py
import paramiko
import argparse
import os
path = "path"
python_script = "worker.py"
# definitions for ssh connection and cluster
ip_list = ['XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX']
port_list = [':XXXX', ':XXXX', ':XXXX']
user_list = ['user', 'user', 'user']
password_list = ['pass', 'pass', 'pass']
node_list = list(map(lambda x: f'-nodex + 1 ', list(range(len(ip_list)))))
cluster = ' '.join([node + ip + port for node, ip, port in zip(node_list, ip_list, port_list)])
# run script on command line of local machine
os.system(f"cd path && python python_script cluster -type worker -index 0 -batch 64 > path/logs/'command output'/ip_list[0].log 2>&1")
# loop for IP and password
stdouts = []
clients = []
for i, (ip, user, password) in enumerate(zip(ip_list[1:], user_list[1:], password_list[1:]), 1):
try:
print("Open session in: " + ip + "...")
client = paramiko.SSHClient()
client.connect(ip, user, password)
except paramiko.SSHException:
print("Connection Failed")
quit()
try:
path = f"C:/Users/user/Desktop/temp-ines"
stdin, stdout, stderr = ssh.exec_command(
f"cd path && python python_script cluster -type worker -index i -batch 64>"
f"C:/Users/user/Desktop/ip.log 2>&1 &"
)
clients.append(ssh)
stdouts.append(stdout)
except paramiko.SSHException:
print("Cannot run file. Continue with other IPs in list...")
client.close()
continue
# Wait for commands to complete
for i in range(len(stdouts)):
print("hello")
stdouts[i].read()
print("hello1")
clients[i].close()
print('hello2")
print("\n\n***********************End execution***********************\n\n")
此脚本在本地运行,能够通过 SSH 连接到服务器并运行命令(即,运行名为 worker.py 的 python 脚本并将命令输出记录到日志文件中)。即,它能够毫无问题地通过第一个 for 循环。
我的问题与第二个 for 循环有关。请参阅我在第二个 for 循环中添加的打印语句以便清楚。当我在本地运行 SSH.py 时,这是我观察到的:
如您所见,我通过 ssh 连接到每台服务器,然后继续阅读我通过 ssh 连接到的第一台服务器的命令输出。 worker.py 脚本可能需要 30 分钟左右才能完成,并且每个服务器上的命令输出都相同——因此需要 30 分钟来读取第一台服务器的命令输出,然后关闭第一台服务器的 SSH 连接,花几秒钟来读取第二台服务器的命令输出(因为它与第一个服务器相同并且已经完全打印),关闭它的 SSH 连接,等等。如果有帮助,请参阅下面的一些命令行输出。
现在,我的问题是,如果我不想等到 worker.py 脚本完成,即整个 30 分钟,该怎么办?我不能/不知道如何提出KeyboardInterrupt exception
。我尝试的是退出本地 SSH.py 脚本。但是,正如您从打印语句中看到的那样,这不会关闭 SSH 连接,尽管训练以及日志文件将停止记录信息。此外,在我退出本地 SSH.py 脚本后,如果我尝试删除任何日志文件,我会收到一条错误消息“无法删除文件,因为它正在 cmd.exe 中使用”——这只有时会发生并且我相信是因为没有关闭 SSH 连接?
首先在 python 控制台中运行:
它挂起:本地 python 和日志文件正在运行和保存,但没有打印语句,并且没有 python 和日志文件正在服务器中运行/保存。
我再次运行它,所以第二个进程开始了:
现在,第一个进程不再挂起(python 运行和日志文件保存在服务器中)。并且可以关闭第二次运行/进程。就像第二次运行/进程有助于解决第一次运行/进程的问题。
如果我在终端中运行python SSH.py
,它只会挂起。
这在以前没有发生过。
【问题讨论】:
【参考方案1】:如果您知道SSHClient.close
完全关闭连接并中止远程命令,请在响应KeyboardInterrupt
时调用它。
为此,您不能使用 stdout.read
的简单解决方案,因为它会阻止并阻止在 Windows 上处理 Ctrl+C。
使用我对Run multiple commands in different SSH servers in parallel using Python Paramiko 的回答中的等待代码(while any(x is not None for x in stdouts):
sn-p)。
并将其包装到try:
...except (KeyboardInterrupt):
。
try:
while any(x is not None for x in stdouts):
for i in range(len(stdouts)):
stdout = stdouts[i]
if stdout is not None:
channel = stdout.channel
# To prevent losing output at the end, first test for exit,
# then for output
exited = channel.exit_status_ready()
while channel.recv_ready():
s = channel.recv(1024).decode('utf8')
print(f"#i stdout: s")
while channel.recv_stderr_ready():
s = channel.recv_stderr(1024).decode('utf8')
print(f"#i stderr: s")
if exited:
print(f"#i done")
clients[i].close()
stdouts[i] = None
time.sleep(0.1)
except (KeyboardInterrupt):
print("Aborting")
for i in range(len(clients)):
print(f"#i closing")
clients[i].close()
如果不需要分离stdout和stderr,使用Channel.set_combine_stderr
可以大大简化代码。见Paramiko ssh die/hang with big output。
【讨论】:
以上是关于当按下 Ctrl+C 时,优雅地中止从 Windows Python Paramiko 脚本通过 SSH 执行的远程 Windows 命令的主要内容,如果未能解决你的问题,请参考以下文章