it-swarm-pt.tech

Como copiar um arquivo para um servidor remoto em Python usando SCP ou SSH?

Eu tenho um arquivo de texto na minha máquina local que é gerado por um script diário do Python executado no cron. 

Eu gostaria de adicionar um pouco de código para ter esse arquivo enviado de forma segura para o meu servidor através de SSH.

85
Alok

Se você quiser a abordagem simples, isso deve funcionar.

Você vai querer ".close ()" o arquivo primeiro para que você saiba que ele é liberado para o disco do Python.

import os
os.system("scp FILE [email protected]:PATH")
#e.g. os.system("scp foo.bar [email protected]:/path/to/foo.bar")

Você precisa gerar (na máquina de origem) e instalar (na máquina de destino) uma chave ssh antecipadamente para que o scp seja autenticado automaticamente com sua chave ssh pública (em outras palavras, para que seu script não solicite uma senha) . 

exemplo ssh-keygen

39
pdq

Para fazer isso em Python (ou seja, não agrupar scp por meio de subprocess.Popen ou similar) com a biblioteca Paramiko , você faria algo assim:

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

(Você provavelmente desejaria lidar com hosts desconhecidos, erros, criar diretórios necessários e assim por diante).

129
Tony Meyer

Você provavelmente usaria o módulo subprocesso . Algo assim:

import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)

Onde destination é provavelmente da forma [email protected]:remotepath. Obrigado a @ Charles Duffy por apontar a fraqueza na minha resposta original, que usava um único argumento de string para especificar a operação scp Shell=True - que não lidaria com espaços em branco em caminhos.

A documentação do módulo tem exemplos de verificação de erros que você pode querer executar em conjunto com esta operação.

Verifique se você configurou as credenciais adequadas para executar um scp autônomo sem senha entre as máquinas . Existe uma questão stackoverflow para isso já .

28
Blair Conrad

Existem algumas maneiras diferentes de abordar o problema:

  1. Enrole programas de linha de comando
  2. use uma biblioteca Python que ofereça recursos SSH (por exemplo, - Paramiko ou Twisted Conch )

Cada abordagem tem suas próprias peculiaridades. Você precisará configurar as chaves SSH para ativar logins sem senha se estiver configurando comandos do sistema como "ssh", "scp" ou "rsync". Você pode inserir uma senha em um script usando a Paramiko ou outra biblioteca, mas talvez a falta de documentação seja frustrante, especialmente se você não estiver familiarizado com os fundamentos da conexão SSH (por exemplo, trocas de chaves, agentes, etc.). Provavelmente, é desnecessário dizer que as chaves SSH são quase sempre uma idéia melhor do que senhas para esse tipo de coisa.

NOTA: É difícil vencer o rsync se você planeja transferir arquivos via SSH, especialmente se a alternativa for scp antigo.

Eu usei a Paramiko com um olho para substituir as chamadas do sistema, mas me vi atraído de volta para os comandos envolvidos devido à sua facilidade de uso e familiaridade imediata. Você pode ser diferente. Eu dei a Conch uma vez mais, mas não me atraiu.

Se optar pelo caminho de chamada do sistema, o Python oferece uma matriz de opções, como os.system ou os módulos de comandos/subprocessos. Eu iria com o módulo subprocessado se usasse a versão 2.4+.

10
Michael

Atingiu o mesmo problema, mas em vez de "hackear" ou emular a linha de comando:

Encontrei esta resposta aqui .

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
Maviles

fabric poderia ser usado para carregar arquivos 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()
3
jfs

Você pode usar o pacote vassal, que é exatamente projetado para isso.

Tudo que você precisa é instalar o vassalo e fazer

from vassal.terminal import Terminal
Shell = Terminal(["scp [email protected]:/home/foo.txt foo_local.txt"])
Shell.run()

Além disso, ele salvará a credencial de autenticação e não precisará digitá-la novamente.

1
Shawn
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')
0
michael

uma abordagem muito simples é a seguinte: 

import os
os.system('sshpass -p "password" scp [email protected]:/path/to/file ./')

nenhuma biblioteca python é necessária (apenas os) e funciona

0
Roberto Marzocchi

Tente isso se você não quiser usar certificados 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!')
0
JavDomGom

Eu usei sshfs para montar o diretório remoto via ssh, e shutil para copiar os arquivos:

$ mkdir ~/sshmount
$ sshfs [email protected]:/path/to/remote/dst ~/sshmount

Então em python:

import shutil
shutil.copy('a.txt', '~/sshmount')

Esse método tem a vantagem de transmitir dados se você estiver gerando dados em vez de fazer o cache localmente e enviar um único arquivo grande.

0
Jonno_FTW

Chamar o comando scp via subprocesso não permite receber o relatório de progresso dentro do script. pexpect poderia ser usado para extrair essa informação:

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

Veja python copia o arquivo na rede local (linux -> linux)

0
jfs