Tôi có một tệp văn bản trên máy cục bộ của mình được tạo bởi một tập lệnh Python hàng ngày chạy bằng cron.
Tôi muốn thêm một chút mã để tệp đó được gửi an toàn đến máy chủ của tôi qua SSH.
Tôi có một tệp văn bản trên máy cục bộ của mình được tạo bởi một tập lệnh Python hàng ngày chạy bằng cron.
Tôi muốn thêm một chút mã để tệp đó được gửi an toàn đến máy chủ của tôi qua SSH.
Câu trả lời:
Bạn có thể gọi scp
lệnh bash (nó sao chép tệp qua SSH ) bằng subprocess.run
:
import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])
Nếu bạn đang tạo tệp mà bạn muốn gửi trong cùng một chương trình Python, bạn sẽ muốn gọi subprocess.run
lệnh bên ngoài with
khối bạn đang sử dụng để mở tệp (hoặc gọi .close()
trên tệp trước nếu bạn không sử dụng with
khối), vì vậy bạn biết nó được chuyển sang đĩa từ Python.
Bạn cần tạo (trên máy nguồn) và cài đặt (trên máy đích) trước một khóa ssh để scp tự động được xác thực bằng khóa ssh chung của bạn (nói cách khác, vì vậy tập lệnh của bạn không yêu cầu mật khẩu) .
subprocess.check_call(['scp', srcfile, dest])
có thể được sử dụng kể từ Python 2.5 thay vìrc = Popen(..).wait(); if rc != 0: raise ..
Để thực hiện điều này bằng Python (tức là không gói scp qua subprocess.Popen hoặc tương tự) với thư viện Paramiko , bạn sẽ làm như sau:
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()
(Bạn có thể muốn xử lý các máy chủ không xác định, lỗi, tạo bất kỳ thư mục nào cần thiết, v.v.).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sau khi khởi tạo ssh
.
Bạn có thể sẽ sử dụng mô-đun quy trình con . Một cái gì đó như thế này:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Nơi destination
có lẽ là của hình thức user@remotehost:remotepath
. Cảm ơn @Charles Duffy đã chỉ ra điểm yếu trong câu trả lời ban đầu của tôi, câu trả lời này sử dụng một đối số chuỗi đơn để chỉ định hoạt động scp shell=True
- sẽ không xử lý khoảng trắng trong đường dẫn.
Tài liệu mô-đun có các ví dụ về kiểm tra lỗi mà bạn có thể muốn thực hiện cùng với thao tác này.
Đảm bảo rằng bạn đã thiết lập thông tin xác thực phù hợp để bạn có thể thực hiện scp không cần giám sát, không cần mật khẩu giữa các máy . Đã có một câu hỏi về stackoverflow cho điều này .
subprocess.check_call(['scp', myfile, destination])
có thể được sử dụng thay thế kể từ Python 2.5 (2006)
Có một số cách khác nhau để tiếp cận vấn đề:
Mỗi cách tiếp cận đều có những điểm kỳ quặc riêng. Bạn sẽ cần thiết lập khóa SSH để kích hoạt đăng nhập không cần mật khẩu nếu bạn đang gói các lệnh hệ thống như "ssh", "scp" hoặc "rsync." Bạn có thể nhúng mật khẩu vào tập lệnh bằng cách sử dụng Paramiko hoặc một số thư viện khác, nhưng bạn có thể cảm thấy khó chịu khi thiếu tài liệu, đặc biệt nếu bạn không quen thuộc với những điều cơ bản của kết nối SSH (ví dụ - trao đổi khóa, tác nhân, v.v.). Có lẽ không cần phải nói rằng khóa SSH hầu như luôn luôn là một ý tưởng tốt hơn so với mật khẩu cho loại nội dung này.
LƯU Ý: rất khó để đánh bại rsync nếu bạn định chuyển tệp qua SSH, đặc biệt nếu giải pháp thay thế là scp cũ.
Tôi đã sử dụng Paramiko với mục đích thay thế các lệnh gọi hệ thống nhưng thấy mình bị thu hút trở lại các lệnh được bọc do tính dễ sử dụng và quen thuộc ngay lập tức. Bạn có thể khác. Tôi đã đưa Conch một lần trước đây nhưng nó không hấp dẫn tôi.
Nếu chọn đường dẫn lệnh gọi hệ thống, Python cung cấp một loạt các tùy chọn như os.system hoặc các lệnh / mô-đun quy trình con. Tôi sẽ đi với mô-đun quy trình con nếu sử dụng phiên bản 2.4+.
Gặp phải vấn đề tương tự, nhưng thay vì "hack" hoặc mô phỏng dòng lệnh:
Tìm thấy câu trả lời này ở đây .
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')
Bạn có thể làm điều gì đó như thế này, để xử lý việc kiểm tra khóa máy chủ lưu trữ
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
có thể được sử dụng để tải lên tệp vis ssh:
#!/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()
Bạn có thể sử dụng gói chư hầu, được thiết kế chính xác cho việc này.
Tất cả những gì bạn cần là cài đặt vassal và làm
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Ngoài ra, nó sẽ giúp bạn tiết kiệm xác thực thông tin đăng nhập và không cần phải nhập lại chúng nhiều lần.
Tôi đã sử dụng sshfs để gắn kết thư mục từ xa qua ssh và tắt để sao chép các tệp:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Sau đó, trong python:
import shutil
shutil.copy('a.txt', '~/sshmount')
Phương pháp này có ưu điểm là bạn có thể truyền trực tuyến dữ liệu nếu bạn đang tạo dữ liệu thay vì lưu vào bộ nhớ đệm cục bộ và gửi một tệp lớn.
Hãy thử điều này nếu bạn không thể sử dụng chứng chỉ SSL:
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!')
Sử dụng paramiko tài nguyên bên ngoài;
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
Lệnh gọi thông qua quy trình con không cho phép nhận báo cáo tiến trình bên trong tập lệnh. pexpect
có thể được sử dụng để trích xuất thông tin đó:
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})
Một cách tiếp cận rất đơn giản như sau:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Không yêu cầu thư viện python (chỉ có hệ điều hành) và nó hoạt động, tuy nhiên việc sử dụng phương pháp này dựa vào một ứng dụng ssh khác được cài đặt. Điều này có thể dẫn đến hành vi không mong muốn nếu chạy trên hệ thống khác.
Loại hacky, nhưng những điều sau đây sẽ hoạt động :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)