Python子进程 - 在SSH上运行多个Shell命令

11

我正在尝试从一个Linux盒子到另一个Linux盒子打开SSH管道,运行一些shell命令,然后关闭SSH。

我无法控制任何一个盒子上的软件包,因此像fabric或paramiko这样的东西是不可行的。

我已经成功地使用以下代码来运行一个bash命令,例如"uptime",但不确定如何依次发出多个命令。我期望的是:

sshProcess = subprocess.call('ssh ' + <remote client>, <subprocess stuff>)
lsProcess = subprocess.call('ls', <subprocess stuff>)
lsProcess.close()
uptimeProcess = subprocess.call('uptime', <subprocess stuff>)
uptimeProcess.close()
sshProcess.close()

我缺少subprocess模块中的哪个部分?

谢谢

pingtest = subprocess.call("ping -c 1 %s" % <remote client>,shell=True,stdout=open('/dev/null', 'w'),stderr=subprocess.STDOUT)
if pingtest == 0:
  print '%s: is alive' % <remote client>
  # Uptime + CPU Load averages
  print 'Attempting to get uptime...'
  sshProcess = subprocess.Popen('ssh '+<remote client>, shell=True,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  sshProcess,stderr = sshProcess.communicate()
  print sshProcess
  uptime = subprocess.Popen('uptime', shell=True,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  uptimeProcess,stderr = uptimeProcess.communicate()
  uptimeProcess.close( )
  print 'Uptime         : ' + uptimeProcess.split('up ')[1].split(',')[0]
else:
  print "%s: did not respond" % <remote client>

你可能想看一下 Fabric,它是一个框架,已经做了你想要做的事情。http://fabric.readthedocs.org/en/1.8/# - Vorsprung
我同意使用Fabric会很有用。不幸的是,我无法控制任何一个盒子,只能访问。 - user2978190
1个回答

24

基本上,如果您调用subprocess,则会创建一个本地子进程而不是远程子进程,因此您应该与ssh进程进行交互。例如以下行: 但请注意,如果您动态构造我的目录,则易受shell注入攻击,然后END行应该是唯一标识符 为避免END行的唯一性问题,最简单的方法是使用不同的ssh命令

from __future__ import print_function,unicode_literals
import subprocess

sshProcess = subprocess.Popen(['ssh',
                               '-tt'
                               <remote client>],
                               stdin=subprocess.PIPE, 
                               stdout = subprocess.PIPE,
                               universal_newlines=True,
                               bufsize=0)
sshProcess.stdin.write("ls .\n")
sshProcess.stdin.write("echo END\n")
sshProcess.stdin.write("uptime\n")
sshProcess.stdin.write("logout\n")
sshProcess.stdin.close()


for line in sshProcess.stdout:
    if line == "END\n":
        break
    print(line,end="")

#to catch the lines up to logout
for line in  sshProcess.stdout: 
    print(line,end="")

我一直收到一个stdin未被识别的错误,所以我添加了模块名称。现在它只是挂起,直到我按下CTRL+C。sshProcess = subprocess.Popen(['ssh', remoteClient],stdin=subprocess.PIPE, stdout = subprocess.PIPE)sshProcess.stdin.write('ls')sshProcess.stdin.write('echo END') for line in sshProcess.stdout.readlines(): if line == "END\n": break print(line)sshProcess.stdin.write("uptime") sshProcess.stdin.write("echo END") for line in sshProcess.stdout.readlines(): if line == "END\n": break print(line)这个格式真是让人头疼。 - user2978190
抱歉在一个旧问题中发帖,但是当我尝试这个时,我在for line in stdout.readlines():这一行上得到了IOError: File not open for reading的错误。我使用了from sys import stdout。我搜索了谷歌,但没有运气。你有什么想法为什么会出现这种情况吗? - Greg
标准输出是sshProcess而不是sys。此外,我进行了测试,发现一切都没有按预期工作。现在这个例子可以正常运行。 - Xavier Combelle
3
虽然这已经是老方法了,但是替代“END技巧”的另一种方法是在最后输入sshProcess.stdin.write("logout\n")。这将关闭ssh连接并且无需在打印sshProcess.stdout时使用“if...break”。 - coradek
完美的答案!谢谢!稍作改进:将 ... Popen(['ssh', '-tt', ... 改为 ... Popen(['ssh', '-t', '-t', ... 可以避免这个错误:Pseudo-terminal will not be allocated because stdin is not a terminal. - mountrix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接