背景: 我正在使用python和paramiko来自动化我每次必须交给一个类的程序时经历的过程。我们使用一个名为“handin”的命令来提交源代码,但这必须从学校的计算机上完成。因此,当我从家中提交代码时,我必须:sftp进入学校服务器,将文件放入目录,ssh进入学校计算机,使用'handin'命令
我可以成功地将文件放到学校的机器上。当我尝试使用exec_command('handin my files')然后读取输出以确定下一个操作时,会出现问题。
所以我有
try:
(stdin, stdout, stderr) = client.exec_command(s)
except:
print 'whoops'
sys.exit()
print stdout.readlines()
但是由于某些原因导致死锁,脚本似乎什么都不做,我最终必须终止整个过程(ctrl + c不起作用)。我不确定exec_command是否未正确完成(即使它已经退出try / catch块),或者我是否有网络问题或者是什么。
有什么想法吗?
更新
问题在于在执行期间与handin命令交互。执行命令后,handin可能仍在运行,也可能不运行。如果它第一次提交它说成功,等等等等,并完成执行。一切都很好。但如果我重新提交,我必须为每个文件授权覆盖(stdin.write('y'))。
TL / DR
如何检查exec_command()是否仍在运行,等待输入,以及相应的stdout readline()?
答案 0 :(得分:11)
我没有看到上面的代码snippit有什么问题。下面的程序是一个完整的脚本,它登录到主机并运行ls命令来查看文件。我只是尝试过,它对我有用。也许尝试一下,看看它是否适合你。如果它不起作用,我怀疑某个特定于您的ssh服务器,正在运行的命令或paramiko安装的问题。如果它对您有用,那么只需对此进行更改即可转向现有功能并查看其中断位置。
import paramiko
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('<ip address here>',username='<username here>',password='<password here>')
stdin,stdout,stderr = ssh.exec_command("ls /")
print stdout.readlines()
如果这对您有用,我的下一个建议是尝试用您尝试运行的实际handin命令替换'ls /'。可能该命令正在等待用户输入等等。
答案 1 :(得分:6)
问题可能是远程命令正在等待输入(它希望你写一些东西给stdin,不知道你不会去,除非你这么说)。试试stdin.channel.shutdown_write()
(我相信stdin.close()
本身不会起作用:那只会导致同花一次)
答案 2 :(得分:0)
这是一个旧线程,但我也看到了此问题。这是由于Paramiko中的错误导致无法通过远程命令处理大型STDOUT而导致的。
参考:https://github.com/paramiko/paramiko/issues/515
可能的解决方案:https://gist.github.com/matjohn2/2c1c4988e17112a34f310667e0ff6e7e
答案 3 :(得分:0)
好的,所以我最近也被这个问题困住了。检查文档和更多搜索让我得到一个可接受的答案,我可以用它来发送输入并检查可能需要用户输入的简单脚本的输出
而不是尝试使用 .read()
或 .readline()
等待直到程序运行完成。
查询他们只要求一个字节,如 .read(1)
或 .readline(1)
直到 stdout.channel.recv_ready()
返回 True
表示是否有东西
要读取的标准输出
使用 stdout.channel.recv(9999)
而不是 realine(1)
可能会更好,因为它允许您请求 9999 个字节而不是 1 个读取/读取行,并且如果您请求的字节数超过现有字节数,您将不会陷入死锁.
http://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.recv_ready
以下只是样板代码,用于查看执行的基本 sudo 命令
ssh.connect(
hostname=host,
port=port,
username=username,
password=password,
)
def readlines(stdout):
line = ''
while sout.channel.recv_ready():
line += sout.readline(1)
return line
>> stdin, stdout, stderr = ssh.exec_command('sudo ls -al /root', get_pty=True)
>> output = readlines(stdout)
>> print(output)
'[sudo] password for user: '
>> if "[sudo] password for" in output:
stdin.write(password + '\n')
stdin.flush()
>> output = readlines(stdout)
>> print(output)
total 88
drwx------ 7 root root 14 May 6 21:40 .
drwxr-xr-x 18 root root 24 Apr 29 18:46 ..
-rw------- 1 root root 3619 May 7 13:19 .bash_history
现在您可以使用它基本上将其扩展到您想要的任何检查。使用 invoke_shell 可能有更好的方法来实现这一点,但这对我有用。