Bir dosyayı SCP veya SSH kullanarak Python'daki uzak bir sunucuya nasıl kopyalarım?


103

Yerel makinemde cron'da çalıştırılan günlük bir Python betiği tarafından oluşturulan bir metin dosyam var.

Bu dosyanın SSH üzerinden sunucuma güvenli bir şekilde gönderilmesi için biraz kod eklemek istiyorum.


1
benzer bir şey buldu, bir bakabilir
misin

Yanıtlar:


55

scpBash komutunu (dosyaları SSH üzerinden kopyalar ) şu şekilde çağırabilirsiniz 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"])

Eğer aynı Python programında göndermek istediğiniz dosyayı oluşturuyorsanız, aramak isteyeceksiniz subprocess.rundışında komutu withdosyayı açmak için kullandığınız bloğun (veya aramayı .close()sen a kullanmıyorsanız ilk dosyada withbloğu), böylece Python'dan diske temizlendiğini bilirsiniz.

Önceden bir ssh anahtarı oluşturmanız (kaynak makinede) ve yüklemeniz (hedef makinede) gerekir, böylece scp otomatik olarak genel ssh anahtarınızla doğrulanır (başka bir deyişle, betiğiniz bir parola istemez) .


3
@CharlesDuffy: subprocess.check_call(['scp', srcfile, dest])yerine Python 2.5'ten beri kullanılabilirrc = Popen(..).wait(); if rc != 0: raise ..
jfs

1
gerekli olmadığında ssh anahtarı eklemek iyi bir çözüm değildir. Örneğin, tek seferlik bir iletişime ihtiyacınız varsa, güvenlik nedeniyle ssh anahtarını ayarlamazsınız.
orezvani

150

Bunu Python'da yapmak için (yani scp'yi alt süreçler , Açık veya benzeri ile kaydırmamak) Paramiko kitaplığı ile yapmak için, şöyle bir şey yaparsınız:

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

(Muhtemelen bilinmeyen ana bilgisayarlar, hatalar, gerekli dizinleri oluşturma vb. İle uğraşmak istersiniz).


14
paramiko'nun güzel bir sftp.put (self, localpath, remotepath, callback = None) işlevi de vardır, bu nedenle her dosyayı yazıp kapatmanıza gerek kalmaz.
Jim Carroll

7
SFTP'nin SCP ile aynı şey olmadığını belirtmek isterim.
Drahkar

4
@Drahkar sorusu dosyanın SSH üzerinden gönderilmesini ister. Bunun yaptığı budur.
Tony Meyer

8
Not: Bunun çalışmasını kutudan çıkarmak için, ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())örneklemeden sonra ekleyin ssh.
doda

1
Çocuklar, sunucu şifresini kullanmak güvenli mi? Özel Anahtarı kullanma imkanı var mı?
Aysennoussi

32

Muhtemelen alt işlem modülünü kullanırsınız . Bunun gibi bir şey:

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

destinationMuhtemelen formun nerede user@remotehost:remotepath. Scp operasyonu belirtmek için tek bir dize argümanı kullanılan orijinal cevap, güçsüzlük işaret için @Charles Duffy sayesinde shell=Trueyollarında boşluk işlemek olmaz -.

Modül belgelerinde, bu işlemle bağlantılı olarak gerçekleştirmek isteyebileceğiniz hata denetimi örnekleri vardır .

Makineler arasında gözetimsiz, şifresiz bir scp gerçekleştirebilmek için doğru kimlik bilgilerini ayarladığınızdan emin olun . Bunun için zaten bir yığın aşımı sorusu var .


3
Alt süreci kullanma: Açık, Doğru Şeydir. Boşluklu dosya adlarının doğru çalışmadığı anlamına geldiği için, bir dizi yerine bir dizge iletmek (ve kabuk = True kullanmak) Yanlış Şeydir.
Charles Duffy

2
"sts = os.waitpid (p.pid, 0)" yerine "sts = p.wait ()" kullanabiliriz.
db42

Bu protokol için doğrudan python API ile yürütmenin ücretsiz bir yolu var mı?
lpapp

1
subprocess.check_call(['scp', myfile, destination])yerine kullanılabilir Python 2.5 (2006)
jfs

@JFSebastian: Evet, Aralık ayında kontrol ettim. Olumlu oylarım en azından bunu kanıtlıyor. :) Yine de takip için teşekkürler.
lpapp

12

Soruna yaklaşmanın birkaç farklı yolu vardır:

  1. Komut satırı programlarını sar
  2. SSH yetenekleri sağlayan bir Python kitaplığı kullanın (örneğin - Paramiko veya Twisted Conch )

Her yaklaşımın kendine özgü tuhaflıkları vardır. "Ssh", "scp" veya "rsync" gibi sistem komutlarını sarmalıyorsanız, şifresiz oturumları etkinleştirmek için SSH anahtarlarını ayarlamanız gerekecektir. Paramiko veya başka bir kitaplığı kullanarak bir şifreyi bir komut dosyasına gömebilirsiniz, ancak özellikle SSH bağlantısının temellerine aşina değilseniz (örn. Anahtar değişimleri, aracılar, vb.), Belge eksikliğini sinir bozucu bulabilirsiniz. Muhtemelen bu tür şeyler için SSH anahtarlarının neredeyse her zaman parolalardan daha iyi bir fikir olduğunu söylemeye gerek yok.

NOT: SSH yoluyla dosya aktarmayı planlıyorsanız, özellikle alternatif düz eski scp ise, rsync'i yenmek zordur.

Paramiko'yu sistem çağrılarını değiştirmeye yönelik bir gözle kullandım, ancak kullanım kolaylığı ve anında aşinalıklarından dolayı sarılmış komutlara geri çekildim. Farklı olabilirsin. Bir süre önce Conch'a bir kereliğine vermiştim ama bana çekici gelmedi.

Sistem çağrısı yolunu seçiyorsanız, Python, os.system veya komutlar / alt işlem modülleri gibi bir dizi seçenek sunar. 2.4+ sürümünü kullanıyorsanız alt işlem modülünü tercih ederim.


1
meraklı: rsync ile scp arasındaki hikaye nedir?
Alok

7

Aynı soruna ulaştım, ancak "hacklemek" veya komut satırına öykünmek yerine:

Bu cevabı burada buldum .

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

1
Not - bu scp paketine bağlıdır. Aralık 2017 itibariyle, bu paketin en son güncellemesi 2015'teydi ve sürüm numarası 0.1.x'tir. Bu bağımlılığı eklemek isteyeceğimden emin değilim.
Chuck

2
yani, şimdi 2018 ve 0.11.0 sürümünü yayınladılar. Öyleyse, SCPClient sonuçta ölmemiş gibi görünüyor?
Adriano_Pinaffo

5

Ana bilgisayar anahtarı kontrolünü de işlemek için buna benzer bir şey yapabilirsiniz.

import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")

4

fabric ssh ile dosya yüklemek için kullanılabilir:

#!/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()

2

Tam olarak bunun için tasarlanmış vasal paketini kullanabilirsiniz.

Tek ihtiyacınız olan vassal yüklemek ve yapmak

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

Ayrıca, kimlik bilgilerinizi de kaydedecek ve tekrar tekrar yazmanıza gerek kalmayacaktır.


1

Uzak dizini ssh yoluyla bağlamak için sshfs kullandım ve dosyaları kopyalamak için shutil kullandım:

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

Sonra python'da:

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

Bu yöntem, verileri yerel olarak önbelleğe almak ve tek bir büyük dosya göndermek yerine veri akışı gerçekleştirme avantajına sahiptir.


1

SSL sertifikalarını kullanmak istemiyorsanız bunu deneyin:

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

1

Harici kaynak paramiko'nun kullanılması;

    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

scpAlt işlem aracılığıyla komut çağırmak , komut dosyası içindeki ilerleme raporunun alınmasına izin vermez. pexpectbu bilgiyi çıkarmak için kullanılabilir:

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

Yerel ağdaki python kopya dosyasına bakın (linux -> linux)


0

Çok basit bir yaklaşım şudur:

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

Python kitaplığı gerekmez (yalnızca os) ve çalışır, ancak bu yöntemi kullanmak başka bir ssh istemcisinin kurulmasına bağlıdır. Bu, başka bir sistemde çalıştırılırsa istenmeyen davranışlara neden olabilir.


-3

Biraz kırılgan, ancak aşağıdakiler işe yaramalı :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.