Pergunta

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

Gostaria de adicionar um pouco de código para que esse arquivo seja enviado com segurança ao meu servidor por SSH.

Foi útil?

Solução

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

Você vai querer ".close()" o arquivo primeiro para saber que ele foi descarregado no disco a partir do Python.

import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. os.system("scp foo.bar joe@srvr.net:/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 automaticamente autenticado com sua chave ssh pública (em outras palavras, para que seu script não solicite uma senha) .

exemplo de ssh-keygen

Outras dicas

Para fazer isso em Python (ou seja,não agrupar scp por meio de subprocess.Popen ou similar) com o Paramiko biblioteca, 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 quaisquer diretórios necessários e assim por diante).

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

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

Onde destination provavelmente é da forma user@remotehost:remotepath.Graças a @Charles Duffy por apontar a fraqueza na minha resposta original, que usou um único argumento de string para especificar a operação SCP shell=True - isso não lidaria com espaços em branco nos 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.

Certifique-se de configurar as credenciais adequadas para poder executar uma scp autônomo e sem senha entre as máquinas.Existe um pergunta stackoverflow para isso já.

Existem algumas maneiras diferentes de abordar o problema:

  1. Quebrar programas de linha de comando
  2. use uma biblioteca Python que fornece recursos SSH (por exemplo - Paramiko ou Concha Torcida)

Cada abordagem tem suas próprias peculiaridades.Você precisará configurar as teclas SSH para ativar logins sem senha se estiver envolvendo comandos do sistema como "ssh", "scp" ou "rsync". Você pode incorporar uma senha em um script usando o Paramiko ou outra biblioteca, mas pode achar a falta de documentação frustrante, especialmente se não estiver familiarizado com o básico da conexão SSH (por exemplo - trocas de chave, agentes, etc.).Provavelmente nem é preciso dizer que as chaves SSH são quase sempre uma ideia melhor do que as senhas para esse tipo de coisa.

OBSERVAÇÃO:é difícil superar o rsync se você planeja transferir arquivos via SSH, especialmente se a alternativa for o antigo scp.

Usei o Paramiko com o objetivo de substituir as chamadas do sistema, mas me senti atraído pelos comandos agrupados devido à sua facilidade de uso e familiaridade imediata.Você pode ser diferente.Dei uma olhada em Conch há algum tempo, mas não me atraiu.

Se optar pelo caminho de chamada do sistema, o Python oferece uma variedade de opções, como os.system ou os módulos de comandos/subprocessos.Eu escolheria o módulo de subprocesso se estivesse usando a versão 2.4+.

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

fabric pode ser usado para fazer upload de 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()

Você pode usar o pacote vassalo, que foi projetado exatamente para isso.

Tudo que você precisa é instalar o vassalo e fazer

from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()

Além disso, você economizará credenciais de autenticação e não precisará digitá-las repetidamente.

Chamando scp O comando 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})

Ver arquivo de cópia python na rede local (linux -> linux)

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

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

$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount

Então em python:

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

Esse método tem a vantagem de poder transmitir dados se estiver gerando dados, em vez de armazená-los em cache localmente e enviar um único arquivo grande.

Tente isto se 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!')

uma abordagem muito simples é a seguinte:

import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')

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

Meio hacky, mas o seguinte deve funcionar :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top