远程服务器上的子进程

Subprocess on remote server

我正在使用此代码在远程服务器上执行命令。

import subprocess
import sys
COMMAND="ls"

ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
                   shell=False,
                   stdout=subprocess.PIPE,
                   stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
    error = ssh.stderr.readlines()
    print >>sys.stderr, "ERROR: %s" % error
else:
    print result

当我尝试执行此脚本时,系统提示我输入密码。有什么办法可以避免它,例如,我可以以某种方式在脚本中输入密码吗?另外,密码应该以某种方式加密,这样有权访问脚本的人就看不到它。

你应该使用 pexpectparamiko 连接到远程机器,然后生成一个 child ,然后 运行 subprocess 来实现你的目标想要。

一种方法是创建一个 public 密钥,将其放在服务器上,然后执行 ssh -i /path/to/pub/key user@host 或使用 paramiko,如下所示:

import paramiko
import getpass

ssh = paramiko.SSHClient()

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

p = getpass.getpass()

ssh.connect('hostname', username='user', password=p)

stdin, stdout, stderr = ssh.exec_command('ls')
print stdout.readlines()
ssh.close()

虽然 subprocess.Popen 可能适用于包装 ssh 访问,但这不是这样做的首选方式。

我推荐使用paramiko

import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(server, username=user,password=password)
...
ssh_client.close()

并且如果你想模拟一个终端,就像用户正在输入一样:

chan=ssh_client.invoke_shell()

def exec_cmd(cmd):
    """Gets ssh command(s), execute them, and returns the output"""
    prompt='bash $' # the command line prompt in the ssh terminal
    buff=''
    chan.send(str(cmd)+'\n')
    while not chan.recv_ready():
        time.sleep(1)
    while not buff.endswith(prompt):
        buff+=ssh_client.chan.recv(1024)
    return buff[:len(prompt)]

用法示例:exec_cmd('pwd')

如果事先不知道提示,可以设置为:

chan.send('PS1="python-ssh:"\n')

这是我之前遇到这个问题时所做的:

  1. 设置用于访问服务器的 ssh 密钥。
  2. Set up an alias for the server you're accessing。下面我就称之为remote_server.
  3. 把下面两行放在~/.bash_profile.

    的末尾
    eval $(ssh-agent -s)
    ssh-add
    

现在每次启动 shell 时,系统都会提示您输入密码。通过输入它,您将验证您的 ssh 密钥并将它们 'in hand' 放在 bash 会话的开始处。在接下来的会话中,您将能够 运行 命令,例如

    ssh remote_server ls

没有提示输入密码。这里 ls 会 运行 在远程服务器上 return 结果给你。同样,如果您从 shell.

执行脚本,您的 python 脚本应该 运行 没有密码提示中断

您还可以通过键入 ssh remote_server 连接到服务器,而无需每次都输入您的用户名或密码。

这样做的好处是您无论如何都应该这样做以避免密码烦恼和记住时髦的服务器名称 :) 此外,您不必担心将密码保存在脚本中的任何位置。唯一的潜在缺点是,如果您想与其他人共享 python 脚本,他们也必须进行此配置(无论如何他们都应该这样做)。

你真的不需要像 pexpect 这样的东西来处理这个。 SSH 密钥已经为这类问题提供了一个非常好的和安全的解决方案。

获得所需结果的最简单方法可能是生成一个 ssh 密钥并将其放在设备的 .ssh 文件夹中。我相信 github 有一个很好的指南,如果你仔细研究的话。一旦您在两个系统上都正确设置了密钥,您实际上不必在代码中添加一行。当您不指定密码时,它会自动使用密钥对您进行身份验证。

为什么搞得这么复杂?这是我的建议:

1) 在您的 ~/.ssh/config 文件中创建一个 ssh 配置部分:

Host myserver
  HostName 50.50.50.12 (fill in with your server's ip)
  Port xxxx (optional)
  User me (your username for server)

2) 如果您已经生成了 ssh 密钥对,请立即执行(使用 ssh-keygen)。然后上传:

$ ssh-copy-id myserver

3) 现在您可以通过 ssh 使用子进程。例如,要捕获输出,我调用:

result = subprocess.check_output(['ssh', 'myserver', 'cat', 'somefile'])

简单、可靠,唯一需要密码的时候是将 public 密钥复制到服务器时。

顺便说一句,使用这些步骤,您的代码可能也能正常工作。

您可以使用以下内容。

import subprocess
import sys
COMMAND="ls"

ssh = subprocess.Popen("powershell putty.exe user@HOST -pw "password", stdout=PIPE, stdin=PIPE, stderr=STDOUT)
result = ssh.stdout.readlines()
if result == []:
    error = ssh.stderr.readlines()
    print >>sys.stderr, "ERROR: %s" % error
else:
    print result