如何通过ssh连接到服务器,并使用python在该服务器上执行bash命令

如何通过ssh连接到服务器,并使用python在该服务器上执行bash命令,python,Python,我想ssh到一个服务器,并在登录到该服务器后在该服务器上执行许多bash命令,我想用python脚本来完成这一点。我仅限于使用子流程(不允许导入其他模块,如pexpect或paramiko) 以下是我目前掌握的代码: import sys import os import subprocess import time user = "let's say a user" host = "the remote server's ip" sshCommand = "sshpass -p 'the r

我想ssh到一个服务器,并在登录到该服务器后在该服务器上执行许多bash命令,我想用python脚本来完成这一点。我仅限于使用子流程(不允许导入其他模块,如pexpect或paramiko) 以下是我目前掌握的代码:

import sys
import os
import subprocess
import time

user = "let's say a user"
host = "the remote server's ip"
sshCommand = "sshpass -p 'the remote server's password' ssh -o     UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no %s@%s" %(user, host)
process1 = subprocess.Popen(sshCommand, shell=True, stdout = subprocess.PIPE)
copyfileCommand = "scp afile.file user@serverip: path of server directory"
process2 = subprocess.Popen(copyfileCommand, shell=True, stdin = process1.stdout, stdout = subprocess.PIPE,  stderr=subprocess.STDOUT)

pwdcommand = "pwd"
process3 = subprocess.Popen(pwdcommand, shell=True, stdin = process2.stdout, stdout=subprocess.PIPE,  stderr=subprocess.STDOUT)
out, err = process3.communicate()[0]
据我所知,要在第一个命令之后执行第二个命令,我需要将第二个命令的stdin设置为第一个命令的stdout,并按照相同的逻辑,对第三个命令执行。但是,当脚本执行到第三个命令时,pwd会给我本地计算机的路径,而不是远程服务器上的路径,并且我要复制到远程服务器的文件也不会被复制。我做错了什么? 这只是我需要在远程服务器上执行的第一个命令,一旦我了解了它的工作原理,其他命令就很容易了


谢谢你

你把一些事情搞混了
Popen
仅在本地主机上工作;如果要执行远程命令,有几个选项:

  • 在命令行上传递要与ssh一起执行的命令
  • 通过stdin将命令传递给远程shell
  • 使用
    paramiko

以交互方式运行远程shell可能很困难,因为您使用的是管道而不是pty(终端)。如果您只想发送一组固定的命令,请将它们写入ssh的stdin,如下所示。如果您希望具有交互性,则需要通过python
pty
模块运行本地ssh。您可以使用pexpect源来查看这是如何完成的

编辑

添加了用于在本地命令上转义密码的代码。我没有在远程命令中添加任何转义,假设OP确实希望传递环境变量之类的内容,但同样的技术也适用

import sys
import os
import subprocess
import time
import pipes

user = "let's say a user"
host = "the remote server's ip"
password = "the remote server's password"
remote_commands = """scp afile.file user@serverip: path of server directory
pwd
"""
sshCommand = "sshpass -p %s ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no %s@%s" % (pipes.quote(password), user, host)
process1 = subprocess.Popen(sshCommand, shell=True, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
out, err = process1.communicate(remote_commands)

如果您想生成一个字符串,您可以通过ssh
ssh
安全地运行一系列远程命令这需要对命令内部命令(必须使用shell调用)进行正确的shell引用,而对外部命令使用
shell=True
进行(因此,可以避免使用具有相同安全含义的本地shell):


与原始公式不同的是,此公式对密码中的恶意内容具有鲁棒性。由于
scp
(从其前身
rcp
复制而来)的设计中存在错误,因此对文件名中的恶意内容具有鲁棒性有些困难;如果是偏执狂,我建议检查包含
字符的源文件名(如果这些在最终产品中参数化)如果存在,则中止操作。

改用
paramiko
模块。整个问题正是Fabric要解决的问题。我认为这里的答案可能会显示任意文件名的正确转义(以在本地和远程shell解释过程中幸存)足够/完整地从Python中为某人提供安全使用
scp
所需的信息。正如@CharlesDuffy所指出的,普通的shell转义规则适用于带有空格或shell元字符的文件名。
sshCommand
仅由本地shell执行,
remotes\u命令
仅由远程shell执行。当
remotes\u命令
包括
scp
时,文件名(或在本例中为“服务器目录路径”)不仅由远程shell解释,还通过名为
serverip
的系统上的glob扩展来解释。将远程服务器的密码作为传递给
子进程的字符串的一部分传入。使用
shell=True
的Popen
,而不是使用
shell=False
的数组元素,也是一种风险。如果密码包含在文本字符中插入“$(rm-rf/)”“?传递一个显式的argv数组可以保护您免受输入的shell解释。回顾这个问题,我发现这些实践是从那里复制而来的,而不是引入的。这是可以原谅的。当然,如果OP想要环境变量或全局扩展,这在远程端不起作用。@tdelaney,确实是--p除非明确要求,否则不要发生这种事情。在命令行上传递要与ssh一起执行的每个命令确实有效,但我的朋友帮助我找到了一种方法,将所有命令都提供给process.stdin。如果将来有人想做类似的事,请参阅此链接:
# Let's say our user is Bobby Tables.
user = 'bobby_tables'
host = 'localhost'
remote_password = """ '"$(rm -rf /)"' """

# commands to run on system named in host variable
commands = [
    ['scp', '/path/to/filename', 'user@thirdhost:/remote/path'],
    ['pwd'],
]
commands_str = '; '.join([' '.join([pipes.quote(word) for word in command])
                          for command in commands])
ssh_command = [
    'sshpass',
    '-p', remote_password,
    'ssh', '-o', 'UserKnownHostsFile=/dev/null',
    '-o', 'StrictHostKeyChecking=no',
    '%s@%s' % (user, host),
    commands_str
]
process1 = subprocess.Popen(ssh_command, stdout=subprocess.PIPE)