Làm thế nào để scp trong Python?


158

Cách pythonic nhất để scp một tệp trong Python là gì? Con đường duy nhất tôi biết là

os.system('scp "%s" "%s:%s"' % (localfile, remotehost, remotefile) )

đó là một hack và không hoạt động bên ngoài các hệ thống giống như Linux và cần sự trợ giúp từ mô-đun PExect để tránh nhắc nhở mật khẩu trừ khi bạn đã cài đặt SSH không mật khẩu vào máy chủ từ xa.

Tôi biết về Twisted conch, nhưng tôi muốn tránh tự mình thực hiện scp thông qua các mô-đun ssh cấp thấp.

Tôi biết paramiko, một mô-đun Python hỗ trợ SSH và SFTP; nhưng nó không hỗ trợ SCP.

Bối cảnh: Tôi đang kết nối với bộ định tuyến không hỗ trợ SFTP nhưng không hỗ trợ SSH / SCP, vì vậy SFTP không phải là một tùy chọn.

EDIT : Đây là bản sao của Cách sao chép tệp vào máy chủ từ xa trong Python bằng SCP hoặc SSH? . Tuy nhiên , câu hỏi đó không đưa ra câu trả lời cụ thể về scp liên quan đến các khóa từ bên trong Python. Tôi hy vọng có một cách để chạy loại mã như

import scp

client = scp.Client(host=host, user=user, keyfile=keyfile)
# or
client = scp.Client(host=host, user=user)
client.use_system_keys()
# or
client = scp.Client(host=host, user=user, password=password)

# and then
client.transfer('/etc/local/filename', '/etc/remote/filename')

Câu trả lời:


106

Hãy thử mô-đun Python scp cho Paramiko . Nó rất dễ sử dụng. Xem ví dụ sau:

import paramiko
from scp import SCPClient

def createSSHClient(server, port, user, password):
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(server, port, user, password)
    return client

ssh = createSSHClient(server, port, user, password)
scp = SCPClient(ssh.get_transport())

Sau đó gọi scp.get()hoặc scp.put()để thực hiện các hoạt động SCP.

( Mã SCPClient )


3
Ngoại trừ mô-đun đó không thực sự sử dụng paramiko - đó là rất nhiều mã mà cuối cùng scpthực sự gọi scpdòng lệnh chỉ hoạt động trên * nix.
Nick Bastin

8
Đồng ý, những gì bạn thấy trong mã nguồn là cuộc gọi từ xa đến scp -thoặc scp -f('đến' và 'từ', tồn tại cho mục đích phía máy chủ này). Đây thực chất là cách scp hoạt động - nó dựa vào một bản sao khác của scp hiện có trên máy chủ. Bài viết này giải thích nó rất tốt: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher

8
Giải pháp này hiệu quả với tôi với hai cảnh báo: 1. đây là những câu lệnh nhập mà tôi đã sử dụng: import paramiko from scp import SCPClient2. Đây là một ví dụ về lệnh scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

Điều này không chỉ hoạt động tuyệt vời, mà còn tận dụng các khóa SSH nếu chúng tồn tại.
Marcel Wilson

Lưu ý rằng bạn cũng có thể cài đặt lib này từ PyPI : pip install scp, tại phiên bản 0.10.2 khi viết bài này.
Andrew B.

15

Bạn có thể quan tâm đến việc dùng thử PExect ( mã nguồn ). Điều này sẽ cho phép bạn xử lý các lời nhắc tương tác cho mật khẩu của bạn.

Đây là một đoạn sử dụng ví dụ (cho ftp) từ trang web chính:

# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')

11

Bạn cũng có thể kiểm tra paramiko . Không có mô-đun scp (chưa), nhưng nó hoàn toàn hỗ trợ sftp.

[EDIT] Xin lỗi, đã bỏ lỡ dòng mà bạn đã đề cập đến paramiko. Các mô-đun sau đây chỉ đơn giản là một triển khai giao thức scp cho paramiko. Nếu bạn không muốn sử dụng paramiko hoặc conch (cách triển khai ssh duy nhất tôi biết cho python), bạn có thể làm lại điều này để chạy qua phiên ssh thông thường bằng cách sử dụng đường ống.

scp.py cho paramiko


Bạn có nói rằng giải pháp đính kèm sẽ chuyển các tệp an toàn sang bất kỳ máy nào đang chạy sshd, ngay cả khi nó không chạy sftpd? Đó chính xác là những gì tôi đang tìm kiếm, nhưng tôi không thể nói từ nhận xét của bạn cho dù điều này chỉ kết thúc sftp trong một mặt tiền giống như scp.
Michael Gundlach

2
Điều này sẽ chuyển các tập tin với bất kỳ máy nào chạy sshd, có scp trong PATH (scp không phải là một phần của thông số ssh, nhưng nó khá phổ biến). Điều này gọi "scp -t" trên máy chủ và sử dụng giao thức scp để truyền tệp, không liên quan gì đến sftp.
JimB

1
Xin lưu ý rằng tôi đã sử dụng triển khai scp của openssh làm mô hình, vì vậy điều này không được đảm bảo để hoạt động với các phiên bản khác. Một số phiên bản của sshd cũng có thể sử dụng giao thức scp2, về cơ bản giống như sftp.
JimB

Bạn có thể vui lòng xem câu hỏi này không: stackoverflow.com/questions/20111242/ Tôi đang tự hỏi liệu paramiko có sử dụng quy trình con hay sao chổi khác như ổ cắm không. Tôi đang gặp vấn đề về bộ nhớ khi sử dụng sub process.checkDefput ('ssh blah@blah.com "cat / data / file *") do sự cố clone / fork và tôi tự hỏi liệu paramiko có gặp vấn đề tương tự hay không?
Paul

1
@Paul: paramiko sẽ không gặp vấn đề tương tự, vì nó không sử dụng quy trình con.
JimB

9

Không thể tìm thấy câu trả lời thẳng và mô-đun "scp.Client" này không tồn tại. Thay vào đó, điều này phù hợp với tôi:

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')

6

nếu bạn cài đặt putty trên win32, bạn sẽ nhận được pscp (putty scp).

vì vậy bạn cũng có thể sử dụng hack os.system trên win32.

(và bạn có thể sử dụng putty-agent để quản lý khóa)


xin lỗi, đây chỉ là một bản hack (nhưng bạn có thể gói nó trong một lớp python)


Lý do duy nhất 'nix one hoạt động là, bạn có SCP trên đường dẫn; như Blauohr chỉ ra, điều đó không quá khó để sửa chữa. +1
ojrac

6

Bạn có thể sử dụng quy trình con gói và lệnh gọi để sử dụng lệnh scp từ shell.

from subprocess import call

cmd = "scp user1@host1:files user2@host2:files"
call(cmd.split(" "))

2
Điều này thực sự khác biệt như thế nào với trường hợp cơ sở được đề cập bởi người hỏi? Đúng, quy trình con tốt hơn os.system, nhưng trong cả hai trường hợp, nó không hoạt động bên ngoài các hệ thống giống như linux, có vấn đề với lời nhắc mật khẩu, v.v.
Foon

5

Cho đến hôm nay, giải pháp tốt nhất có lẽ là AsyncSSH

https://asyncssh.readthedocs.io/en/latest/#scp-client

async with asyncssh.connect('host.tld') as conn:
    await asyncssh.scp((conn, 'example.txt'), '.', recurse=True)

1
Chào. Bạn đưa ra tuyên bố về AsyncSSH là tốt nhất, nhưng bạn có thể đưa ra tuyên bố hoặc 2 để hỗ trợ cho yêu cầu này không? Có thể phân biệt nó với scp () hoặc cách nó quan trọng đối với trường hợp sử dụng của bạn. (Điều này nói rằng, đó là mã ít hơn rất nhiều - ít nhất là trong ví dụ của bạn)
Scott Prive

2
@Crossfit_and_Beer xin chào. Vì vậy, tôi đã nói "có lẽ là tốt nhất", tôi sẽ để mọi người phán xét :) Tôi không có nhiều thời gian để so sánh AsyncSSH với các thư viện khác. Nhưng rất nhanh: sử dụng đơn giản, không đồng bộ, được bảo trì và trình bảo trì rất hay và hữu ích, nó khá đầy đủ và được ghi chép lại rất tốt.
Loïc

5

Có một cái nhìn vào vải.transfer .

from fabric import Connection

with Connection(host="hostname", 
                user="admin", 
                connect_kwargs={"key_filename": "/home/myuser/.ssh/private.key"}
               ) as c:
    c.get('/foo/bar/file.txt', '/tmp/')

2
Bạn có thể đặc sắc hơn không?
Nick T

4

Đã khá lâu kể từ khi câu hỏi này được hỏi, và trong khi đó, một thư viện khác có thể xử lý vấn đề này đã bị cắt: Bạn có thể sử dụng chức năng sao chép có trong thư viện Plumbum :

import plumbum
r = plumbum.machines.SshMachine("example.net")
   # this will use your ssh config as `ssh` from shell
   # depending on your config, you might also need additional
   # params, eg: `user="username", keyfile=".ssh/some_key"`
fro = plumbum.local.path("some_file")
to = r.path("/path/to/destination/")
plumbum.path.utils.copy(fro, to)

Làm cách nào tôi có thể nhập cụm mật khẩu cho khóa riêng bằng Plumbum? p
ekta

@ekta: Bạn sẽ được nhắc về nó trên dòng lệnh. Nếu bạn muốn cung cấp nó từ mã của riêng bạn, tôi không nghĩ API của plumbum hỗ trợ điều này. Nhưng plumbum SshMachinekhông có quyền truy cập ssh-agent, vì vậy nếu bạn chỉ muốn tránh gõ nó mỗi lần, đó là một cách để làm điều đó. Bạn cũng có thể gửi yêu cầu tính năng trên trang github của plumbum.
pmos

2

Nếu bạn đang sử dụng * nix, bạn có thể sử dụng sshpass

sshpass -p password scp -o User=username -o StrictHostKeyChecking=no src dst:/path

2
import paramiko

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

client.connect('<IP Address>', username='<User Name>',password='' ,key_filename='<.PEM File path')

#Setup sftp connection and transmit this script 
print ("copying")

sftp = client.open_sftp() 
sftp.put(<Source>, <Destination>)


sftp.close()

Sẽ rất hữu ích nếu bạn thêm một số bình luận vào câu trả lời của mình giải thích lý do tại sao giải pháp của bạn là một giải pháp tốt và giúp người đọc so sánh nó với các giải pháp khác có sẵn. Chỉ đăng mã không phải lúc nào cũng giúp người học biết cách nhận ra các giải pháp tốt hơn.
Brian Tompsett - 汤

Đây là SFTP, không phải SCP.
Martin Prikryl

1

Hmmm, có lẽ một tùy chọn khác sẽ là sử dụng một cái gì đó như sshfs (cũng có một sshfs cho Mac). Khi bộ định tuyến của bạn được gắn kết, bạn có thể sao chép các tập tin hoàn toàn. Tôi không chắc chắn nếu nó hoạt động cho ứng dụng cụ thể của bạn nhưng đó là một giải pháp tốt để giữ cho tiện dụng.


1

Tôi trong khi trước đây tôi đã tập hợp một kịch bản sao chép SCP python phụ thuộc vào paramiko. Nó bao gồm mã để xử lý các kết nối với khóa riêng hoặc tác nhân khóa SSH với dự phòng xác thực mật khẩu.

http://code.activestate.com/recipes/576810-copy-files-over-ssh-USE-paramiko/


Cảm ơn liên kết và đã dành thời gian để xuất bản ví dụ tốt về việc sử dụng paramiko.
Peter M. - là viết tắt của Monica

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.