Como scp em Python?


158

Qual é a maneira mais pitônica de scp um arquivo em Python? A única rota que eu conheço é

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

que é um hack e que não funciona fora de sistemas semelhantes ao Linux, e que precisa de ajuda do módulo Pexpect para evitar solicitações de senha, a menos que você já tenha o SSH sem senha configurado para o host remoto.

Estou ciente do Twisted conch, mas prefiro evitar a implementação do scp por meio de módulos ssh de baixo nível.

Estou ciente de paramikoum módulo Python que suporta SSH e SFTP; mas não suporta SCP.

Antecedentes: estou me conectando a um roteador que não suporta SFTP, mas suporta SSH / SCP, portanto, o SFTP não é uma opção.

Edição : Esta é uma duplicata de como copiar um arquivo para um servidor remoto em Python usando SCP ou SSH? . No entanto , essa pergunta não fornece uma resposta específica do scp que lida com as chaves do Python. Estou esperando uma maneira de executar o código como

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

Respostas:


106

Experimente o módulo scp do Python para o Paramiko . É muito fácil de usar. Veja o seguinte exemplo:

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

Em seguida, chame scp.get()ou scp.put()faça operações SCP.

( Código SCPClient )


3
Exceto que o módulo não usa o paramiko - é um monte de código que, no final, scpacaba chamando a scplinha de comando que funciona apenas no * nix.
Nick Bastin

8
De acordo, o que você vê no código-fonte é a chamada remota para os modos ( scp -tou scp -f'para' e 'de', que existem para esse fim do servidor). É essencialmente assim que o scp funciona - ele depende de outra cópia do scp existente no servidor. Este artigo explica muito bem: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher 26/10/13

8
Esta solução funcionou para mim com duas ressalvas: 1. estas são as declarações de importação que eu usei: import paramiko from scp import SCPClient2. Aqui está um exemplo do comando scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

Isso não só funciona muito bem, mas também tira proveito das chaves SSH, se elas existirem.
Marcel Wilson

Observe que você também pode instalar esta lib a partir do PyPI pip install scp:, na versão 0.10.2 até o momento da redação deste documento.
Andrew B.

15

Você pode estar interessado em experimentar o Pexpect ( código fonte ). Isso permitiria lidar com solicitações interativas para sua senha.

Aqui está um exemplo de uso de exemplo (para ftp) no site principal:

# 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

Você também pode conferir o paramiko . Ainda não existe um módulo scp, mas ele suporta totalmente o sftp.

[EDIT] Desculpe, perdeu a linha onde você mencionou o paramiko. O módulo a seguir é simplesmente uma implementação do protocolo scp para paramiko. Se você não quiser usar o paramiko ou o conch (as únicas implementações ssh que eu conheço para python), você pode refazer isso para executar uma sessão ssh regular usando pipes.

scp.py para paramiko


Você está dizendo que a solução anexada transferirá arquivos com segurança para qualquer máquina que esteja executando o sshd, mesmo que não esteja executando o sftpd? É exatamente isso que estou procurando, mas não posso dizer pelo seu comentário se isso apenas envolve o sftp em uma fachada do tipo scp.
Michael Gundlach

2
Isso transferirá arquivos com qualquer máquina executando sshd, que possui scp no PATH (scp não faz parte da especificação ssh, mas é bastante onipresente). Isso chama "scp -t" no servidor e usa o protocolo scp para transferir arquivos, o que não tem nada a ver com sftp.
21710 JimB

1
Observe que eu usei a implementação do scp do openssh como modelo, portanto não é garantido que funcione com outras versões. Algumas versões do sshd também podem usar o protocolo scp2, que é basicamente o mesmo que sftp.
21768 JimB

Você poderia ver esta pergunta: stackoverflow.com/questions/20111242/… Gostaria de saber se o paramiko usa subprocessos ou algo parecido com soquetes. Estou tendo problemas de memória usando subprocess.check_output ('ssh blah@blah.com "cat / data / file *") devido a problemas de clone / fork e estou imaginando se o paramiko terá os mesmos problemas ou não?
Paul

1
@ Paul: paramiko não terá os mesmos problemas, pois não usa um subprocesso.
JimB

9

Não foi possível encontrar uma resposta direta e esse módulo "scp.Client" não existe. Em vez disso, isso combina comigo:

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

se você instalar o putty no win32, receberá um pscp (putty scp).

então você também pode usar o os.system hack no win32.

(e você pode usar o agente putty para o gerenciamento de chaves)


desculpe, é apenas um hack (mas você pode envolvê-lo em uma classe python)


A única razão pela qual o 'nix one funciona é: você tem SCP no caminho; como Blauohr aponta, isso não é muito difícil de corrigir. 1
ojrac 30/10/08

6

Você pode usar o subprocesso do pacote e a chamada de comando para usar o comando scp no shell.

from subprocess import call

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

2
Como isso é substancialmente diferente do caso base mencionado pelo questionador? Sim, subprocess é melhor do que os.system, mas em ambos os casos, ele não funciona fora linux-like sistemas, tem problemas com solicitações de senha, etc.
Foon

5

Hoje, a melhor solução é provavelmente 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
Oi. Você afirma que o AsyncSSH é o melhor, mas poderia fazer uma declaração ou 2 para apoiar essa afirmação? Talvez diferencie de scp () ou como isso importava para o seu caso de uso. (Dito isto, é muito menos código - no seu exemplo, pelo menos)
Scott Prive

2
@Crossfit_and_Beer hello. Então eu disse "provavelmente o melhor", deixarei o julgamento para qualquer pessoa :) Não tenho muito tempo agora para comparar o AsyncSSH com outras bibliotecas. Mas muito rapidamente: é simples de usar, é assíncrono, é mantido e o mantenedor é muito agradável e útil, é bastante completo e muito bem documentado.
Loïc

5

Dê uma olhada no fabric.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
Você poderia ser mais específico?
Nick T

4

Já faz um bom tempo que essa pergunta foi feita e, entretanto, surgiu outra biblioteca que pode lidar com isso: Você pode usar a função de cópia incluída na biblioteca 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)

Como posso inserir a senha da chave privada com o Plumbum? p
ekta

@ekta: Você será solicitado na linha de comando. Se você deseja fornecê-lo a partir do seu próprio código, não acho que a API do plumbum suporte isso. Mas o Plumbum's SshMachinetem acesso ssh-agent; portanto, se você quiser evitar digitá-lo toda vez, é uma maneira de fazê-lo. Você também pode registrar uma solicitação de recurso na página github do plumbum.
OGP

3
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()

Ajudaria se você adicionasse algum comentário à sua resposta, explicando por que sua solução é boa e ajudando os leitores a compará-la com as outras soluções disponíveis. Apenas postar código nem sempre ajuda o aluno a reconhecer melhores soluções.
Brian Tompsett - quarta-feira

Este é SFTP, não SCP.
Martin Prikryl

2

Se você estiver no * nix, poderá usar o sshpass

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

1

Hmmm, talvez outra opção seria usar algo como sshfs (também existe um sshfs para Mac). Depois que o roteador estiver montado, você poderá copiar os arquivos imediatamente. Não tenho certeza se isso funciona para o seu aplicativo em particular, mas é uma boa solução para se manter à mão.



Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.