使用Python在ssh上执行命令

时间:2010-08-27 16:09:02

标签: python ssh

我正在编写一个脚本来自动化Python中的一些命令行命令。目前我正在打电话:

cmd = "some unix command"
retcode = subprocess.call(cmd,shell=True)

但是我需要在远程计算机上运行一些命令。手动,我会使用ssh登录然后运行命令。我如何在Python中自动执行此操作?我需要使用(已知的)密码登录到远程计算机,所以我不能只使用cmd = ssh user@remotehost,我想知道是否有我应该使用的模块?

16 个答案:

答案 0 :(得分:161)

我会推荐你​​paramiko

请参阅this question

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)

答案 1 :(得分:44)

或者你可以使用commands.getstatusoutput

   commands.getstatusoutput("ssh machine 1 'your script'")

我广泛使用它并且效果很好。

在Python 2.6+中,使用subprocess.check_output

答案 2 :(得分:26)

你看过Fabric了吗?它允许您使用python通过SSH执行各种远程操作。

答案 3 :(得分:15)

我发现paramiko有点太低级了,而且Fabric不太适合用作库,所以我把我自己的库spur放在一起,使用paramiko来实现更好的接口:

import spur

shell = spur.SshShell(hostname="localhost", username="bob", password="password1")
result = shell.run(["echo", "-n", "hello"])
print result.output # prints hello

如果你需要在shell中运行:

shell.run(["sh", "-c", "echo -n hello"])

答案 4 :(得分:7)

保持简单。无需库。

import subprocess

subprocess.Popen("ssh {user}@{host} {cmd}".format(user=user, host=host, cmd='ls -l'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

答案 5 :(得分:6)

所有人都已经说过(推荐)使用 paramiko ,我只是共享一个python代码(可能会说API),它允许你一次执行多个命令。

在不同节点上执行命令:Commands().run_cmd(host_ip, list_of_commands)

  

您将看到一个TODO,如果任何命令无法执行,我会一直停止执行,我不知道该怎么做。请分享您的知识

#!/usr/bin/python

import os
import sys
import select
import paramiko
import time


class Commands:
    def __init__(self, retry_time=0):
        self.retry_time = retry_time
        pass

    def run_cmd(self, host_ip, cmd_list):
        i = 0
        while True:
        # print("Trying to connect to %s (%i/%i)" % (self.host, i, self.retry_time))
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(host_ip)
            break
        except paramiko.AuthenticationException:
            print("Authentication failed when connecting to %s" % host_ip)
            sys.exit(1)
        except:
            print("Could not SSH to %s, waiting for it to start" % host_ip)
            i += 1
            time.sleep(2)

        # If we could not connect within time limit
        if i >= self.retry_time:
            print("Could not connect to %s. Giving up" % host_ip)
            sys.exit(1)
        # After connection is successful
        # Send the command
        for command in cmd_list:
            # print command
            print "> " + command
            # execute commands
            stdin, stdout, stderr = ssh.exec_command(command)
            # TODO() : if an error is thrown, stop further rules and revert back changes
            # Wait for the command to terminate
            while not stdout.channel.exit_status_ready():
                # Only print data if there is data to read in the channel
                if stdout.channel.recv_ready():
                    rl, wl, xl = select.select([ stdout.channel ], [ ], [ ], 0.0)
                    if len(rl) > 0:
                        tmp = stdout.channel.recv(1024)
                        output = tmp.decode()
                        print output

        # Close SSH connection
        ssh.close()
        return

def main(args=None):
    if args is None:
        print "arguments expected"
    else:
        # args = {'<ip_address>', <list_of_commands>}
        mytest = Commands()
        mytest.run_cmd(host_ip=args[0], cmd_list=args[1])
    return


if __name__ == "__main__":
    main(sys.argv[1:])

谢谢!

答案 6 :(得分:4)

我使用paramiko一堆(很好)和pxssh(也很好)。我会推荐。它们的工作方式略有不同,但在使用方面有较大的重叠。

答案 7 :(得分:2)

接受的答案对我不起作用,这是我改用的方式:

import paramiko
import os

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# ssh.load_system_host_keys()
ssh.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
ssh.connect("d.d.d.d", username="user", password="pass", port=22222)

ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -alrt")
exit_code = ssh_stdout.channel.recv_exit_status() # handles async exit error 

for line in ssh_stdout:
    print(line.strip())

total 44
-rw-r--r--.  1 root root  129 Dec 28  2013 .tcshrc
-rw-r--r--.  1 root root  100 Dec 28  2013 .cshrc
-rw-r--r--.  1 root root  176 Dec 28  2013 .bashrc
...

或者,您可以使用sshpass

import subprocess
cmd = """ sshpass -p "myPas$" ssh user@d.d.d.d -p 22222 'my command; exit' """
print( subprocess.getoutput(cmd) )

参考文献:
1. https://github.com/onyxfish/relay/issues/11
2. https://stackoverflow.com/a/61016663/797495

答案 8 :(得分:0)

查看spurplus,我们围绕spur开发的包装器提供类型注释和一些小噱头(重新连接SFTP,md5 。):{{3 }}

答案 9 :(得分:0)

paramiko 在添加其他行之后终于为我工作,这确实很重要(第3行):

import paramiko

p = paramiko.SSHClient()
p.set_missing_host_key_policy(paramiko.AutoAddPolicy())   # This script doesn't work for me unless this line is added!
p.connect("server", port=22, username="username", password="password")
stdin, stdout, stderr = p.exec_command("your command")
opt = stdout.readlines()
opt = "".join(opt)
print(opt)

确保已安装paramiko软件包。 解决方案的原始来源:Source

答案 10 :(得分:0)

完美工作...

import paramiko
import time

ssh = paramiko.SSHClient()
#ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.106.104.24', port=22, username='admin', password='')

time.sleep(5)
print('connected')
stdin, stdout, stderr = ssh.exec_command(" ")

def execute():
       stdin.write('xcommand SystemUnit Boot Action: Restart\n')
       print('success')

execute()

答案 11 :(得分:0)

您可以使用以下任何命令,这也将帮助您输入密码。

cmd = subprocess.run(["sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@domain.com ps | grep minicom"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(cmd.stdout)
OR
cmd = subprocess.getoutput("sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@domain.com ps | grep minicom")
print(cmd)

答案 12 :(得分:0)

第一:我很惊讶还没有人提到 fabric

第二:对于您描述的那些要求,我已经实现了一个名为 jk_simpleexec 的自己的 Python 模块。目的:让运行命令变得简单。

让我为你解释一下。

“在本地执行命令”问题

我的 python 模块 jk_simpleexec 提供了一个名为 runCmd(..) 的函数,它可以在本地或远程执行 shell (!) 命令。这很简单。以下是本地执行命令的示例:

import jk_simpleexec

cmdResult = jk_simpleexec.runCmd(None, "cd / ; ls -la")

注意:请注意,默认情况下会自动修剪返回的数据,以从 STDOUT 和 STDERR 中删除过多的空行。 (当然,可以停用此行为,但出于您所想的目的,该行为正是您想要的。)

“处理结果”问题

您将收到一个包含返回码、STDOUT 和 STDERR 的对象。因此处理结果非常容易。

这就是您想要做的,因为您执行的命令可能存在并已启动,但可能无法执行预期的操作。在您对 STDOUT 和 STDERR 不感兴趣的最简单情况下,您的代码可能如下所示:

cmdResult.raiseExceptionOnError("Something went wrong!", bDumpStatusOnError=True)

出于调试目的,您希望在某个时间将结果输出到 STDOUT,因此您可以这样做:

cmdResult.dump()

如果您想处理 STDOUT,它也很简单。示例:

for line in cmdResult.stdOutLines:
    print(line)

“远程执行命令”问题

现在当然我们可能想在另一个系统上远程执行这个命令。为此,我们可以以完全相同的方式使用相同的函数 runCmd(..),但我们需要先指定一个 fabric 连接对象。可以这样做:

from fabric import Connection

REMOTE_HOST = "myhost"
REMOTE_PORT = 22
REMOTE_LOGIN = "mylogin"
REMOTE_PASSWORD = "mypwd"
c = Connection(host=REMOTE_HOST, user=REMOTE_LOGIN, port=REMOTE_PORT, connect_kwargs={"password": REMOTE_PASSWORD})

cmdResult = jk_simpleexec.runCmd(c, "cd / ; ls -la")

# ... process the result stored in cmdResult ...

c.close()

一切都保持不变,但这次我们在另一台主机上运行此命令。这是意图:我想要一个统一的 API,如果您在某个时候决定从本地主机移动到另一台主机,则不需要对软件进行任何修改。

密码输入问题

现在当然有密码问题。上面已经有一些用户提到了这一点:我们可能想向执行此 python 代码的用户询问密码。

对于这个问题,我很久以前就创建了一个自己的模块。 jk_pwdinput。与常规密码输入的不同之处在于 jk_pwdinput 会输出一些星星而不是什么都不打印。因此,对于您输入的每个密码字符,您都会看到一个星号。这样您输入密码就更容易了。

代码如下:

import jk_pwdinput

# ... define other 'constants' such as REMOTE_LOGIN, REMOTE_HOST ...

REMOTE_PASSWORD = jk_pwdinput.readpwd("Password for " + REMOTE_LOGIN + "@" + REMOTE_HOST + ": ")

(为了完整起见:如果 readpwd(..) 返回 None,则用户使用 Ctrl+C 取消密码输入。在现实世界中,您可能希望对此采取适当的行动。)

完整示例

这是一个完整的例子:

import jk_simpleexec
import jk_pwdinput
from fabric import Connection

REMOTE_HOST = "myhost"
REMOTE_PORT = 22
REMOTE_LOGIN = "mylogin"
REMOTE_PASSWORD = jk_pwdinput.readpwd("Password for " + REMOTE_LOGIN + "@" + REMOTE_HOST + ": ")
c = Connection(host=REMOTE_HOST, user=REMOTE_LOGIN, port=REMOTE_PORT, connect_kwargs={"password": REMOTE_PASSWORD})

cmdResult = jk_simpleexec.runCmd(
    c = c,
    command = "cd / ; ls -la"
)
cmdResult.raiseExceptionOnError("Something went wrong!", bDumpStatusOnError=True)

c.close()

最后的笔记

所以我们有全套:

  • 执行命令,
  • 通过相同的 API 远程执行该命令,
  • 通过输入密码以简单安全的方式创建连接。

上面的代码对我来说很好地解决了这个问题(希望对你也是如此)。而且一切都是开源的:Fabric 是 BSD-2-Clause,我自己的模块在 Apache-2 下提供。

使用的模块:

快乐编码! ;-)

答案 13 :(得分:-1)

在下面的示例中,如果要用户输入主机名,用户名,密码和端口号。

{{1}}

答案 14 :(得分:-1)

#Reading the Host,username,password,port from excel file
import paramiko 
import xlrd

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0,1)
Port = int(sheet.cell_value(3,1))
User = sheet.cell_value(1,1)
Pass = sheet.cell_value(2,1)

def details(Host,Port,User,Pass):
    ssh.connect(Host, Port, User, Pass)
    print('connected to ip ',Host)
    stdin, stdout, stderr = ssh.exec_command("")
    stdin.write('xcommand SystemUnit Boot Action: Restart\n')
    print('success')

details(Host,Port,User,Pass)

答案 15 :(得分:-1)

要求用户根据他们登录的设备输入命令。
以下代码已通过PEP8online.com验证。

import paramiko
import xlrd
import time

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0, 1)
Port = int(sheet.cell_value(3, 1))
User = sheet.cell_value(1, 1)
Pass = sheet.cell_value(2, 1)

def details(Host, Port, User, Pass):
    time.sleep(2)
    ssh.connect(Host, Port, User, Pass)
    print('connected to ip ', Host)
    stdin, stdout, stderr = ssh.exec_command("")
    x = input('Enter the command:')
    stdin.write(x)
    stdin.write('\n')
    print('success')

details(Host, Port, User, Pass)