SCPまたはSSHを使用してPythonでリモートサーバーにファイルをコピーする方法


103

私のローカルマシンには、cronで実行される毎日のPythonスクリプトによって生成されるテキストファイルがあります。

そのファイルをSSH経由でサーバーに安全に送信するためのコードを少し追加したいと思います。


1
似た何かを見つけ、uが見て持つことができます stackoverflow.com/questions/11009308/...
JJ84

回答:


55

scpbashコマンドを呼び出すことができます(SSH経由でファイルをコピーします)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"])

同じPythonプログラムで送信するファイルを作成する場合は、ファイルを開くために使用しているブロックのsubprocess.run外でコマンドを呼び出すwith必要があります(または.close()、ファイルを使用していない場合は、最初にファイルを呼び出します)withブロック)、Pythonからディスクにフラッシュされることがわかります。

scpが公開sshキーで自動的に認証されるように(つまり、スクリプトがパスワードを要求しないように)事前にsshキーを(ソースマシンで)生成し、(宛先マシンで)インストールする必要があります。 。


3
@CharlesDuffy:subprocess.check_call(['scp', srcfile, dest])代わりにPython 2.5以降で使用可能rc = Popen(..).wait(); if rc != 0: raise ..
jfs

1
sshキーを追加する必要がない場合は、適切なソリューションではありません。たとえば、1回限りの通信が必要な場合は、セキュリティ上の理由から、sshキーを設定しません。
orezvani 14年

150

これをPythonで(つまり、scpをsubprocess.Popenなどでラップしないで)Paramikoライブラリで行うには、次のようにします。

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

(おそらく、不明なホスト、エラー、必要なディレクトリの作成などに対処する必要があります)。


14
paramikoにもsftp.put(self、localpath、remotepath、callback = None)関数があり、書き込みを開いたり、各ファイルを閉じたりする必要はありません。
ジムキャロル

6
SFTPはSCPと同じではないことに注意してください。
Drahkar

4
@Drahkar質問では、ファイルをSSH経由で送信するように求められます。それがこれです。
Tony Meyer、

8
注:これをすぐに機能させるにはssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())、インスタンス化後に追加しますssh
doda 2012年

1
みんな、サーバーのパスワードを使用しても安全ですか?秘密鍵を使用する可能性はありますか?
Aysennoussi 2014

32

おそらくサブプロセスモジュールを使用します。このようなもの:

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

どこでdestination、おそらく形式ですuser@remotehost:remotepath。単一の文字列引数を使用してscp操作を指定した、元の回答の弱点を指摘してくれた@Charles Duffyに感謝しshell=Trueます。これは、パスの空白を処理しません。

モジュールのドキュメントには、この操作と組み合わせて実行したいエラーチェックの例が含まれています。

マシン間で無人のパスワードなしのscpを実行できるように、適切な資格情報を設定したことを確認してください。これにはすでにスタックオーバーフローの質問があります。


3
subprocess.Popenの使用は正しいことです。配列ではなく文字列を渡すこと(およびshell = Trueを使用すること)は、スペースを含むファイル名が正しく機能しないことを意味するため、間違っています。
Charles Duffy、

2
「sts = os.waitpid(p.pid、0)」の代わりに、「sts = p.wait()」を使用できます。
db42

このプロトコルの直接python APIを使用して、それを実行しない方法はありますか?
lpapp

1
subprocess.check_call(['scp', myfile, destination])Python 2.5(2006)以降では代わりに使用できます
jfs '28

@JFSebastian:うん、12月にチェックアウトした。私の賛成票は、少なくともそれを証明しています。:)しかし、フォローアップをありがとう。
lpapp 14年

12

この問題に対処するには、いくつかの方法があります。

  1. コマンドラインプログラムをラップする
  2. SSH機能を提供するPythonライブラリを使用します(例:ParamikoまたはTwisted Conch

それぞれのアプローチには独自の癖があります。「ssh」、「scp」、「rsync」などのシステムコマンドをラップする場合は、パスワードなしのログインを有効にするためにSSHキーを設定する必要があります。Paramikoまたは他のライブラリを使用してスクリプトにパスワードを埋め込むことができますが、特にSSH接続の基本(例:鍵交換、エージェントなど)に精通していない場合は、ドキュメントが不足していることに苛立ちを感じるかもしれません。言うまでもなく、SSHキーは、ほとんどの場合、この種のものについてはパスワードよりも優れたアイデアです。

注:SSH経由でファイルを転送する予定がある場合、特に代替がプレーンな古いscpである場合は、rsyncに勝るのは困難です。

私はシステムコールの置き換えに目を向けてParamikoを使用しましたが、使いやすさとすぐに慣れ親しんでいたため、ラップされたコマンドに引き込まれました。あなたは違うかもしれません。しばらく前にコンシュを与えましたが、それは私にはアピールしませんでした。

システムコールパスを選択した場合、Pythonはos.systemやコマンド/サブプロセスモジュールなどのオプションの配列を提供します。バージョン2.4以降を使用している場合は、サブプロセスモジュールを使用します。


1
好奇心が強い:rsync対scpのストーリーは何ですか?
Alok、

7

同じ問題に到達しましたが、「ハッキング」やコマンドラインのエミュレーションの代わりに:

ここでこの答えを見つけまし

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
注-これはscpパッケージに依存しています。2017年12月現在、このパッケージの最新の更新は2015年で、バージョン番号は0.1.xです。この依存関係を追加するかどうかはわかりません。
Chuck

2
そのため、2018年になり、5月にバージョン0.11.0がリリースされました。結局のところ、SCPClientは死んでいないようです。
Adriano_Pinaffo

5

ホストキーチェックも処理するために、このようなことを行うことができます

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

4

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

2

このために設計されたvassalパッケージを使用できます。

必要なのは家臣をインストールして行うことだけです

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

また、認証情報が節約され、何度も入力する必要がありません。


1

私が使用sshfsのを ssh経由でリモートディレクトリをマウントし、するshutilファイルをコピーするには:

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

次に、python:

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

この方法には、ローカルにキャッシュして1つの大きなファイルを送信するのではなく、データを生成する場合にデータをストリーミングできるという利点があります。


1

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

1

外部リソース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')

0

scpサブプロセスを介してコマンドを呼び出すと、スクリプト内で進捗レポートを受け取ることができません。pexpectその情報を抽出するために使用できます:

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

ローカルネットワークのpythonコピーファイルを参照(linux-> linux)


0

非常に単純なアプローチは次のとおりです。

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

Pythonライブラリは必要ありません(OSのみ)。これは機能しますが、この方法を使用するには、別のsshクライアントをインストールする必要があります。これは、別のシステムで実行した場合、望ましくない動作を引き起こす可能性があります。


-3

ハックのようなものですが、以下はうまくいくはずです:)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.