SCP または SSH を使用して Python でファイルをリモート サーバーにコピーするにはどうすればよいですか?
-
09-06-2019 - |
質問
ローカル マシン上に、cron で毎日実行される Python スクリプトによって生成されたテキスト ファイルがあります。
そのファイルを SSH 経由でサーバーに安全に送信できるように、コードを少し追加したいと思います。
解決
シンプルなアプローチが必要な場合は、これでうまくいくはずです。
まずファイルを「.close()」して、ファイルが Python からディスクにフラッシュされたことを確認します。
import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. os.system("scp foo.bar joe@srvr.net:/path/to/foo.bar")
scp が公開 ssh キーで自動的に認証されるように (つまり、スクリプトがパスワードを要求しないように)、事前に ssh キーを (ソース マシン上で) 生成し、(宛先マシン上で) インストールする必要があります。 。
他のヒント
これを Python で行うには (つまり、サブプロセス.Popen などを介して scp をラップしない) パラミコ ライブラリの場合、次のようなことを行います。
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()
(おそらく、不明なホスト、エラー、必要なディレクトリの作成などに対処する必要があるでしょう)。
おそらく使用するでしょう サブプロセスモジュール. 。このようなもの:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
どこ destination
おそらく次のような形です user@remotehost:remotepath
. 。SCP操作を指定するために単一の文字列引数を使用した私の元の答えの弱点を指摘してくれた@CharlesDuffyに感謝します shell=True
- それはパス内の空白を処理しません。
モジュールのドキュメントには、 この操作と組み合わせて実行できるエラー チェックの例を示します。
を実行できるように、適切な資格情報が設定されていることを確認してください。 マシン間の無人、パスワードなしの SCP. 。があります これについてはすでにスタックオーバーフローの質問があります.
この問題に対処するには、いくつかの異なる方法があります。
それぞれのアプローチには独自の癖があります。「SSH」、「SCP」、「RSYNC」などのシステムコマンドをラップしている場合、パスワードレスログインを有効にするためにSSHキーをセットアップする必要があります。 Paramikoまたは他のライブラリを使用してスクリプトにパスワードを埋め込むことはできますが、特にSSH接続の基本(例えば、キー交換、エージェントなど)に慣れていない場合は、ドキュメントの不足がイライラすることがあります。おそらく言うまでもないことですが、この種のものでは、ほとんどの場合、パスワードよりも SSH キーの方が優れています。
注記:SSH 経由でファイルを転送する予定がある場合、特に代替手段が単純な古い scp である場合、rsync に勝つのは困難です。
私はシステム コールの置き換えを目的として Paramiko を使用してきましたが、その使いやすさとすぐに馴染みやすいため、ラップされたコマンドに戻ってしまうことに気づきました。あなたは違うかもしれません。だいぶ前にコンチをあげたんですけど、自分には魅力的じゃなかったんです。
システム コール パスを選択する場合、Python は os.system やコマンド/サブプロセス モジュールなどの一連のオプションを提供します。バージョン 2.4 以降を使用している場合は、subprocess モジュールを使用します。
同じ問題に到達しましたが、「ハッキング」またはコマンドラインをエミュレートする代わりに、次のようにします。
この答えが見つかりました ここ.
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
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()
まさにこのために設計された家臣パッケージを使用できます。
必要なのは、家臣をインストールして実行することだけです
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
また、認証資格情報が保存されるため、認証資格情報を何度も入力する必要がありません。
電話をかける 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})
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')
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!')
非常に簡単なアプローチは次のとおりです。
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Pythonライブラリは必要なく(OSのみ)、動作します
ちょっとハックっぽいですが、以下はうまくいくはずです:)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)