scp
(セキュアコピー)コマンドをsubprocess.Popen
を使用して実行しようとしています。ログインにはパスワードを送信する必要があります。
from subprocess import Popen, PIPE
proc = Popen(['scp', "[email protected]:/foo/bar/somefile.txt", "."], stdin = PIPE)
proc.stdin.write(b'mypassword')
proc.stdin.flush()
これはすぐにエラーを返します:
[email protected]'s password:
Permission denied, please try again.
私は確かですパスワードは正しいです。シェルでscp
を手動で呼び出すことで簡単に確認できます。では、なぜこれが機能しないのですか?
subprocess.Popen
について質問し、自動化されたSSHまたはFTPログインのパスワードを送信することについて、これと同様の多くの質問があることに注意してください。
pythonスクリプト)からLinuxでユーザーパスワードを設定するにはどうすればよいですか?
サブプロセスを使用してパスワードを送信する
Python 3.を使用しているため、これらの質問に対する回答が機能しないか、適用されません。
リンクした2番目の回答は、Pexpectを使用することを提案しています(これは通常、入力を期待するコマンドラインプログラムと対話するための正しい方法です)。 fork があり、使用できるpython3で動作します。
ssh
を使用したパスワード付きのpexpect
への関数を次に示します。
import pexpect
def ssh(Host, cmd, user, password, timeout=30, bg_run=False):
"""SSH'es to a Host using the supplied credentials and executes a command.
Throws an exception if the command doesn't return 0.
bgrun: run command in the background"""
fname = tempfile.mktemp()
fout = open(fname, 'w')
options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
if bg_run:
options += ' -f'
ssh_cmd = 'ssh %s@%s %s "%s"' % (user, Host, options, cmd)
child = pexpect.spawn(ssh_cmd, timeout=timeout) #spawnu for Python 3
child.expect(['[pP]assword: '])
child.sendline(password)
child.logfile = fout
child.expect(pexpect.EOF)
child.close()
fout.close()
fin = open(fname, 'r')
stdout = fin.read()
fin.close()
if 0 != child.exitstatus:
raise Exception(stdout)
return stdout
scp
を使用すると、同様のことが可能になるはずです。
OpenSSH scp
ユーティリティはssh
プログラムを呼び出してリモートホストへのSSH接続を確立し、sshプロセスが認証を処理します。 ssh
ユーティリティは、コマンドラインまたはその標準入力でパスワードを受け入れません。これはOpenSSH開発者側の意図的な決定だと思います。なぜなら、人々はキーベースの認証のようなより安全なメカニズムを使用すべきだと感じているからです。 sshを呼び出すためのソリューションはすべて、次のいずれかのアプローチに従います。
ssh
を取得し、 here または here で説明されている別のコマンドを呼び出すことにより、または一部の回答でパスワードを取得します- ここ 。ssh
の修正バージョンをビルドして、希望どおりに動作させます。この特定のケースでは、pythonスクリプトからscp
をすでに呼び出していることを考えると、これらの1つが最も合理的なアプローチであると思われます。
Pexpectには、まさにこのためのライブラリがあります:pxssh
http://pexpect.readthedocs.org/en/stable/api/pxssh.html
import pxssh
import getpass
try:
s = pxssh.pxssh()
hostname = raw_input('hostname: ')
username = raw_input('username: ')
password = getpass.getpass('password: ')
s.login(hostname, username, password)
s.sendline('uptime') # run a command
s.Prompt() # match the Prompt
print(s.before) # print everything before the Prompt.
s.logout()
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login.")
print(e)
一部のアプリケーションはstdinを使用してユーザーと対話し、一部のアプリケーションは端末を使用して対話すると思います。この場合、PIPEを使用してパスワードを書き込むときは、stdinに書き込みます。しかし、SCPアプリケーションは端末からパスワードを読み取ります。サブプロセスはターミナルを使用してユーザーと対話することはできませんが、stdinを使用してのみ対話することができるため、サブプロセスモジュールを使用することはできず、scpを使用してファイルをコピーするためにpexpectを使用する必要があります。
自由に修正してください。
これが、pexpectに基づく私のscp関数です。パスワードに加えて、ワイルドカード(つまり、複数のファイル転送)を処理できます。複数のファイル転送(つまり、ワイルドカード)を処理するには、シェルを介してコマンドを発行する必要があります。 pexpect FAQ を参照してください。
import pexpect
def scp(src,user2,Host2,tgt,pwd,opts='',timeout=30):
''' Performs the scp command. Transfers file(s) from local Host to remote Host '''
cmd = f'''/bin/bash -c "scp {opts} {src} {user2}@{Host2}:{tgt}"'''
print("Executing the following cmd:",cmd,sep='\n')
tmpFl = '/tmp/scp.log'
fp = open(tmpFl,'wb')
childP = pexpect.spawn(cmd,timeout=timeout)
try:
childP.sendline(cmd)
childP.expect([f"{user2}@{Host2}'s password:"])
childP.sendline(pwd)
childP.logfile = fp
childP.expect(pexpect.EOF)
childP.close()
fp.close()
fp = open(tmpFl,'r')
stdout = fp.read()
fp.close()
if childP.exitstatus != 0:
raise Exception(stdout)
except KeyboardInterrupt:
childP.close()
fp.close()
return
print(stdout)
次のように使用できます。
params = {
'src': '/home/src/*.txt',
'user2': 'userName',
'Host2': '192.168.1.300',
'tgt': '/home/userName/',
'pwd': myPwd(),
'opts': '',
}
scp(**params)