我在本地计算机上有一个文本文件,它由在cron中运行的每日Python脚本生成。
我想添加一些代码,以便通过SSH将该文件安全地发送到我的服务器。
要使用Paramiko库在Python中执行此操作(即不通过subprocess.Popen或类似方法包装scp),您可以执行以下操作:
1 2 3 4 5 6 7 8 9 10
| import os
import paramiko
ssh = paramiko.SSHClient()
ssh.load_host_keys(os.path.expanduser(os.path.join("~",".ssh","known_hosts")))
ssh.connect(server, username=username, password=password)
sftp = ssh.open_sftp()
sftp.put(localpath, remotepath)
sftp.close()
ssh.close() |
(您可能希望处理未知主机,错误,创建所需的任何目录,等等)。
如果你想要简单的方法,这应该工作。
你首先想要".close()"这个文件,所以你知道它是从Python刷新到磁盘的。
1 2 3
| import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. os.system("scp foo.bar joe@srvr.net:/path/to/foo.bar") |
您需要生成(在源计算机上)并预先安装(在目标计算机上)ssh密钥,以便scp自动使用您的公共ssh密钥进行身份验证(换句话说,因此您的脚本不会要求输入密码) 。
ssh-keygen示例
您可能使用子进程模块。像这样的东西:
1 2 3
| import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0) |
其中destination可能是user@remotehost:remotepath的形式。谢谢
@Charles Duffy指出我原来答案中的弱点,它使用单个字符串参数来指定scp操作shell=True - 它不会处理路径中的空格。
模块文档包含您可能希望与此操作一起执行的错误检查示例。
确保您已设置正确的凭据,以便可以在计算机之间执行无人值守的无密码scp。有一个stackoverflow问题已经存在。
有几种不同的方法可以解决这个问题:
包装命令行程序
使用提供SSH功能的Python库(例如 - Paramiko或Twisted Conch)
每种方法都有自己的怪癖。如果要包装"ssh","scp"或"rsync"等系统命令,则需要设置SSH密钥以启用无密码登录。您可以使用Paramiko或其他库在脚本中嵌入密码,但您可能会发现缺少文档令人沮丧,特别是如果您不熟悉SSH连接的基础知识(例如 - 密钥交换,代理等)。毫无疑问,SSH密钥几乎总是比这类东西的密码更好。
注意:如果您计划通过SSH传输文件,它很难击败rsync,特别是如果替代方案是普通的旧scp。
我已经使用了Paramiko来关注更换系统调用,但由于易于使用和直接熟悉,我发现自己被回归到包装的命令。你可能会有所不同。我不久前给了海螺一次,但它并没有吸引我。
如果选择系统调用路径,Python提供了一系列选项,如os.system或命令/子进程模块。如果使用版本2.4+,我会使用子进程模块。
达到了同样的问题,但不是"黑客"或模拟命令行:
在这里找到这个答案。
1 2 3 4 5 6 7 8 9 10
| from paramiko import SSHClient
from scp import SCPClient
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('example.com')
with SCPClient(ssh.get_transport()) as scp:
scp.put('test.txt', 'test2.txt')
scp.get('test2.txt') |
您可以执行类似的操作,以处理主机密钥检查
1 2
| import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path") |
fabric可用于通过ssh上传文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #!/usr/bin/env python
from fabric.api import execute, put
from fabric.network import disconnect_all
if __name__=="__main__":
import sys
# specify hostname to connect to and the remote/local paths
srcdir, remote_dirname, hostname = sys.argv[1:]
try:
s = execute(put, srcdir, remote_dirname, host=hostname)
print(repr(s))
finally:
disconnect_all() |
您可以使用专为此设计的vassal包。
你所需要的只是安装vassal并做
1 2 3
| from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run() |
此外,它将为您节省身份验证凭据,而无需一次又一次地键入它们。
如果您不想使用SSL证书,请尝试此操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import subprocess
try:
# Set scp and ssh data.
connUser = 'john'
connHost = 'my.host.com'
connPath = '/home/john/'
connPrivateKey = '/home/user/myKey.pem'
# Use scp to send file from local to host.
scp = subprocess.Popen(['scp', '-i', connPrivateKey, 'myFile.txt', '{}@{}:{}'.format(connUser, connHost, connPath)])
except CalledProcessError:
print('ERROR: Connection to host failed!') |
我使用sshfs通过ssh挂载远程目录,并使用shutil复制文件:
1 2
| $ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount |
然后在python中:
1 2
| import shutil
shutil.copy('a.txt', '~/sshmount') |
此方法的优点是,如果要生成数据而不是本地缓存并发送单个大文件,则可以流式传输数据。
一个非常简单的方法如下:
1 2
| import os
os.system('sshpass -p"password" scp user@host:/path/to/file ./') |
不需要python库(只有操作系统),它的工作原理
使用外部资源paramiko;
1 2 3 4 5 6 7 8 9
| from paramiko import SSHClient
from scp import SCPClient
import os
ssh = SSHClient()
ssh.load_host_keys(os.path.expanduser(os.path.join("~",".ssh","known_hosts")))
ssh.connect(server, username='username', password='password')
with SCPClient(ssh.get_transport()) as scp:
scp.put('test.txt', 'test2.txt') |
通过子进程调用scp命令不允许在脚本中接收进度报告。 pexpect可用于提取该信息:
1 2 3 4 5 6 7 8 9 10
| import pipes
import re
import pexpect # $ pip install pexpect
def progress(locals):
# extract percents
print(int(re.search(br'(\d+)%$', locals['child'].after).group(1)))
command ="scp %s %s" % tuple(map(pipes.quote, [srcfile, destination]))
pexpect.run(command, events={r'\d+%': progress}) |
查看本地网络中的python copy文件(linux - > linux)
有点hacky,但以下应该工作:)
1 2 3 4
| import os
filePath ="/foo/bar/baz.py"
serverPath ="/blah/boo/boom.py"
os.system("scp"+filePath+" user@myserver.com:"+serverPath) |