example - ¿Cómo copiar un archivo a un servidor remoto en Python usando SCP o SSH?
python ssh library (11)
Tengo un archivo de texto en mi equipo local que se genera mediante un script diario de Python ejecutado en cron.
Me gustaría agregar un poco de código para que ese archivo se envíe de forma segura a mi servidor a través de SSH.
Alcanzó el mismo problema, pero en lugar de "piratear" o emular la línea de comandos:
Encontré esta respuesta here .
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'')
Hay un par de formas diferentes de abordar el problema:
- Ajustar programas de línea de comandos
- use una biblioteca de Python que proporcione capacidades SSH (p. ej., Paramiko o concha retorcida )
Cada enfoque tiene sus propias peculiaridades. Tendrá que configurar las claves SSH para habilitar inicios de sesión sin contraseña si está envolviendo comandos del sistema como "ssh", "scp" o "rsync". Puede insertar una contraseña en un script utilizando Paramiko o alguna otra biblioteca, pero puede encontrar frustrante la falta de documentación, especialmente si no está familiarizado con los conceptos básicos de la conexión SSH (por ejemplo, intercambios de claves, agentes, etc.). Probablemente no hace falta decir que las claves SSH son casi siempre una mejor idea que las contraseñas para este tipo de cosas.
NOTA: es difícil de superar rsync si planea transferir archivos a través de SSH, especialmente si la alternativa es simple viejo scp.
Utilicé Paramiko con miras a reemplazar las llamadas al sistema, pero me di cuenta de que me sentía atraído por los comandos envueltos debido a su facilidad de uso y familiaridad inmediata. Usted puede ser diferente. Le di la vuelta a Conch hace algún tiempo pero no me atraía.
Si opta por la ruta de la llamada al sistema, Python ofrece una matriz de opciones, como os.system o los comandos / subprocesos. Iré con el módulo de subproceso si uso la versión 2.4+.
Llamar al comando scp
través de un subproceso no permite recibir el informe de progreso dentro del script. pexpect
podría usarse para extraer esa información:
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 el archivo de copia Python en la red local (linux -> linux)
Para hacer esto en Python (es decir, no envolviendo scp a través del subproceso.Popen o similar) con la biblioteca Paramiko , harías algo como esto:
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()
(Probablemente desee tratar con hosts desconocidos, errores, crear directorios necesarios, etc.).
Probablemente usaría el módulo de subproceso . Algo como esto:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Donde el destination
es probablemente del formulario user@remotehost:remotepath
. Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que usaba un argumento de cadena simple para especificar la operación de scp shell=True
- eso no manejaría el espacio en blanco en las rutas.
La documentación del módulo contiene ejemplos de verificación de errores que es posible que desee realizar junto con esta operación.
Asegúrese de haber configurado las credenciales adecuadas para que pueda realizar un scp desatendido sin contraseña entre las máquinas . Ya hay una pregunta de para esto .
Si quieres un enfoque simple, esto debería funcionar.
Querrá ".close ()" el archivo primero para que sepa que está en el disco de Python.
import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. os.system("scp foo.bar [email protected]:/path/to/foo.bar")
Necesita generar (en la máquina fuente) e instalar (en la máquina de destino) una clave ssh de antemano para que el scp se autentique automáticamente con su clave ssh pública (en otras palabras, para que su script no solicite una contraseña) .
Tipo de hacky, pero el siguiente debería funcionar :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" [email protected]:"+serverPath)
un enfoque muy simple es el siguiente:
import os
sshpass -p "password" scp user@host:/path/to/file ./
no se requiere ninguna biblioteca de Python (solo sistema operativo) y funciona
fabric
se puede usar para cargar archivos 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()
sshfs para montar el directorio remoto a través de ssh y shutil para copiar los archivos:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Luego en Python:
import shutil
shutil.copy(''a.txt'', ''~/sshmount'')
Este método tiene la ventaja de que puede transmitir datos si está generando datos en lugar de almacenar en caché localmente y enviar un solo archivo grande.
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'')