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')
Michael Gundlach
fonte

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 )

Tom Shen
fonte
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 ('[email protected]')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')
Pat Notz
fonte
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

JimB
fonte
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 [email protected] "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')
Maviles
fonte
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)

Blauohr
fonte
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(" "))
user7529863
fonte
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)
Loïc
fonte
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/')
user443854
fonte
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)
pmos
fonte
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()
shrikkanth roxor
fonte
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
user178047
fonte
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.

Pat Notz
fonte