Mam plik tekstowy na mojej maszynie lokalnej, który jest generowany przez codzienny skrypt Pythona uruchamiany w cronie.
Chciałbym dodać trochę kodu, aby ten plik był bezpiecznie przesyłany na mój serwer przez SSH.
Mam plik tekstowy na mojej maszynie lokalnej, który jest generowany przez codzienny skrypt Pythona uruchamiany w cronie.
Chciałbym dodać trochę kodu, aby ten plik był bezpiecznie przesyłany na mój serwer przez SSH.
Odpowiedzi:
Możesz wywołać scp
polecenie bash (kopiuje pliki przez SSH ) za pomocą subprocess.run
:
import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])
Jeśli tworzysz plik, który chcesz wysłać w tym samym programie w języku Python, będziesz chciał wywołać subprocess.run
polecenie poza with
blokiem, którego używasz do otwarcia pliku (lub .close()
najpierw wywołaj plik, jeśli nie używasz with
block), więc wiesz, że jest on opróżniany na dysk z Pythona.
Musisz wcześniej wygenerować (na maszynie źródłowej) i zainstalować (na maszynie docelowej) klucz ssh, aby scp został automatycznie uwierzytelniony za pomocą twojego publicznego klucza ssh (innymi słowy, aby twój skrypt nie pytał o hasło) .
subprocess.check_call(['scp', srcfile, dest])
może być używany od Pythona 2.5 zamiastrc = Popen(..).wait(); if rc != 0: raise ..
Aby to zrobić w Pythonie (tj. Nie owijając scp przez subprocess.Popen lub podobną) z biblioteką Paramiko , zrobiłbyś coś takiego:
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()
(Prawdopodobnie chciałbyś poradzić sobie z nieznanymi hostami, błędami, utworzeniem wszelkich niezbędnych katalogów i tak dalej).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
po utworzeniu wystąpienia ssh
.
Prawdopodobnie użyłbyś modułu podprocesu . Coś takiego:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Gdzie destination
prawdopodobnie jest forma user@remotehost:remotepath
. Podziękowania dla @Charles Duffy za wskazanie słabości mojej pierwotnej odpowiedzi, która użyła pojedynczego argumentu ciągu do określenia operacji scp shell=True
- która nie obsługuje białych znaków w ścieżkach.
Dokumentacja modułu zawiera przykłady sprawdzania błędów, które możesz chcieć wykonać w połączeniu z tą operacją.
Upewnij się, że skonfigurowano odpowiednie poświadczenia, aby można było wykonać nienadzorowany, bezhasłowy scp między komputerami . Jest już pytanie o przepełnienie stosu .
subprocess.check_call(['scp', myfile, destination])
może być używany zamiast tego od Pythona 2.5 (2006)
Istnieje kilka różnych sposobów podejścia do problemu:
Każde podejście ma swoje dziwactwa. Będziesz musiał skonfigurować klucze SSH, aby włączyć logowanie bez hasła, jeśli pakujesz polecenia systemowe, takie jak „ssh”, „scp” lub „rsync”. Możesz osadzić hasło w skrypcie za pomocą Paramiko lub innej biblioteki, ale brak dokumentacji może być frustrujący, szczególnie jeśli nie znasz podstaw połączenia SSH (np. - wymiana kluczy, agenci itp.). Prawdopodobnie jest oczywiste, że klucze SSH są prawie zawsze lepszym pomysłem niż hasła do tego rodzaju rzeczy.
UWAGA: trudno jest pokonać rsync, jeśli planujesz przesyłać pliki przez SSH, zwłaszcza jeśli alternatywą jest zwykły stary scp.
Użyłem Paramiko z zamiarem zastąpienia wywołań systemowych, ale wróciłem do opakowanych poleceń ze względu na ich łatwość użycia i natychmiastową znajomość. Możesz być inny. Dałem Conch raz jeszcze jakiś czas temu, ale nie przemówiło to do mnie.
Jeśli zdecydujesz się na ścieżkę wywołań systemowych, Python oferuje tablicę opcji, takich jak os.system lub moduły commands / subprocess. Wybrałbym moduł podprocesu, jeśli używasz wersji 2.4+.
Napotkano ten sam problem, ale zamiast „hakowania” lub emulacji wiersza poleceń:
Znalazłem tę odpowiedź tutaj .
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')
Możesz zrobić coś takiego, aby obsłużyć również sprawdzanie klucza hosta
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
może zostać użyty do przesłania plików 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()
Możesz użyć pakietu wasala, który jest dokładnie do tego przeznaczony.
Wszystko, czego potrzebujesz, to zainstalować wasala i zrobić
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Ponadto zapisze ci dane uwierzytelniające i nie musisz ich wpisywać wielokrotnie.
Użyłem sshfs do zamontowania zdalnego katalogu przez ssh i shutil do skopiowania plików:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Następnie w Pythonie:
import shutil
shutil.copy('a.txt', '~/sshmount')
Ta metoda ma tę zaletę, że możesz przesyłać strumieniowo dane, jeśli generujesz dane zamiast buforować lokalnie i wysyłać pojedynczy duży plik.
Spróbuj tego, jeśli nie chcesz używać certyfikatów 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!')
Korzystanie z zewnętrznego zasobu paramiko;
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')
Wywołanie scp
polecenia za pośrednictwem podprocesu nie pozwala na otrzymanie raportu postępu wewnątrz skryptu. pexpect
można użyć do wyodrębnienia tych informacji:
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})
Bardzo proste podejście jest następujące:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Żadna biblioteka Pythona nie jest wymagana (tylko system operacyjny) i działa, jednak użycie tej metody wymaga zainstalowania innego klienta ssh. Może to spowodować niepożądane zachowanie, jeśli zostanie uruchomione w innym systemie.
Trochę hacky, ale poniższe powinny działać :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)