pythonサブプロセスモジュールを使用して安全なFTPサイトにログインし、ファイルを取得しようとしています。しかし、要求されたときにパスワードを送信しようとするだけでハングアップし続けます。私は今のところ次のコードを持っています:
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password')
これはまだパスワードプロンプトで停止します。パスワードを手動で入力すると、ftpサイトに移動し、コマンドラインにパスワードを入力します。私は人々がpexpectの使用を提案するのを見てきましたが、長い話をすれば、標準ライブラリソリューションが必要です。とにかくサブプロセスや他のstdlibを使用していますか?上記の何を忘れていますか?
試す
proc.stdin.write('yourPassword\n')
proc.stdin.flush()
うまくいくはずです。
stdin=None
子プロセスは親のstdinを継承します(your Python program))。
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate(input='password')
通信でinput = ‘password’を使用してみてください。これでうまくいきました。
サブプロセスアプローチを廃止し、sftpアクセスにparamikoパッケージを使用することをお勧めします。
SFTPにはParamiko
を使用します。それ以外の場合、これは機能します:
import subprocess
args = ['command-that-requires-password', '-user', 'me']
proc = subprocess.Popen(args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
proc.stdin.write('mypassword\n')
proc.stdin.flush()
stdout, stderr = proc.communicate()
print stdout
print stderr
これを行う最も安全な方法は、事前にパスワードを要求してから、それをコマンドにパイプすることです。パスワードを要求することで、コードのどこかにパスワードを保存する必要がなくなります。次に例を示します。
from getpass import getpass
from subprocess import Popen, PIPE
password = getpass("Please enter your password: ")
proc = Popen("sftp user@server stop".split(), stdin=PIPE)
# Popen only accepts byte-arrays so you must encode the string
proc.communicate(password.encode())
これは、pexpectではなく、expectを使用した純粋なPythonソリューションです。
Ubuntuの場合、最初にインストールする必要があります。
Sudo apt install expect
Python 3.6以降:
def sftp_rename(from_name, to_name):
sftp_password = 'abigsecret'
sftp_username = 'foo'
destination_hostname = 'some_hostname'
from_name = 'oldfilename.txt'
to_name = 'newfilename.txt'
commands = f"""
spawn sftp -o "StrictHostKeyChecking no"
{sftp_username}@{destination_hostname}
expect "password:"
send "{sftp_password}\r"
expect "sftp>"
send "rename {from_name} {to_name}\r"
expect "sftp>"
send "bye\r"
expect "#"
"""
sp = subprocess.Popen(['expect', '-c', commands], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
パスワードの改行(ユーザーがEnterキーを押す)を忘れただけです。
_from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password\n'.encode())
_
また、.encode()
はデフォルトでproc.communicate()
がバイトのようなオブジェクトを受け入れるためです。
これと同じ問題が1週間悩みました。コマンドインジェクションの脆弱性の導入を回避しようとしたため、サブプロセスを通じてユーザー入力からパスワードを安全に送信する必要がありました。これが、同僚の助けを借りて問題を解決した方法です。
import subprocess
command = ['command', 'option1', '--password']
subprocess.Popen(command, stdin=subprocess.PIPE).wait(timeout=60)
.wait(timeout = int)は、ユーザーが入力をstdinに送ることができるため、最も重要なコンポーネントでした。それ以外の場合、タイムアウトはデフォルトで0になり、ユーザーが入力を入力する時間がないため、Noneまたはnull文字列になります。これを理解するために永遠に私を取りました。
これを複数回行う必要があることがわかっている繰り返しのユースケースでは、popen関数をオーバーライドしてプライベートメソッドとして使用できます。他の誰かが興味を持つと予想される場合は、同じプログラマーから教えられたベストプラクティスです。後でコードを保守する際に、コードをいじりたくありません。
def _popen(cmd):
proc_h = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc_h.wait(timeout=60)
return proc_h.poll() == os.EX_OK
ユーザーが入力を求められる場合は、stdout = subprocess.PIPEを削除することが重要です。それ以外の場合は、プロセスが60秒間ハングしたように見え、ユーザーはプロンプトを表示せず、パスワードを入力する必要があることを認識しません。 stdoutは当然シェルウィンドウに移動し、ユーザーが入力をpopen()に渡すことを許可します。
また、proc_h.poll()== os.EX_OKを返す理由を説明するためだけに、コマンドが成功した場合は0を返します。これは、関数が失敗した場合にシステムエラーコードを返したい場合のcスタイルのベストプラクティスであり、0がインタープリターによって "false"として扱われることを考慮に入れています。
import subprocess
args = ['command', 'arg1', 'arg2']
proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
proc.stdin.write(b'password') ##The b prefix is necessary because it needs a byte type
proc.stdin.flush()
stdout, stderr = proc.communicate()
print(stdout)
print(stderr)