Ответ 1
Возможно, вместо этого вы должны использовать expect -like?
Например Pexpect (пример). Существуют и другие аналогичные библиотеки python.
Я пытаюсь использовать модуль подпроцесса python для входа на защищенный ftp-сайт, а затем захватить файл. Тем не менее, я постоянно зависаю, просто пытаюсь отправить пароль, когда он запрашивается. У меня до сих пор есть следующий код:
from subprocess import Popen, PIPE
proc = Popen(['sftp','[email protected]', 'stop'], stdin=PIPE)
proc.communicate('password')
Это все еще останавливается при запросе пароля. Если я вручную введу пароль, он перейдет на ftp-сайт и затем вводит пароль в командной строке. Я видел, как люди предлагают использовать pexpect, но длинный рассказ. Мне нужно стандартное библиотечное решение. Есть ли в любом случае подпроцесс и/или любой другой stdlib? Что я забыл выше?
Возможно, вместо этого вы должны использовать expect -like?
Например Pexpect (пример). Существуют и другие аналогичные библиотеки python.
Try
proc.stdin.write('yourPassword\n')
proc.stdin.flush()
Это должно работать.
То, что вы описываете, похоже на stdin=None
, где дочерний процесс наследует stdin родителя (ваша программа Python).
from subprocess import Popen, PIPE
proc = Popen(['sftp','[email protected]', 'stop'], stdin=PIPE)
proc.communicate(input='password')
Попробуйте с вводом = 'пароль в общении, который работал для меня.
Я бы рекомендовал отказаться от подпроцесса и использовать пакет paramiko для доступа sftp.
Используйте Paramiko
для SFTP. Для чего-либо еще это работает:
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
Эта та же самая проблема мучила меня в течение недели. Мне пришлось безопасно вводить пароль из пользовательского ввода через подпроцесс, потому что я пытался избежать введения уязвимости внедрения команд. Вот как я решил проблему с небольшой помощью коллеги.
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 будет интерпретироваться интерпретатором как "ложный".
Это чистое решение Python, использующее ожидаемый, а не pexpect.
Если в 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)
Самый безопасный способ сделать это - запросить пароль заранее и затем передать его в команду. Запрос пароля не позволит сохранить пароль где-либо в вашем коде. Вот пример:
from getpass import getpass
from subprocess import Popen, PIPE
password = getpass("Please enter your password: ")
proc = Popen("sftp [email protected] stop".split(), stdin=PIPE)
# Popen only accepts byte-arrays so you must encode the string
proc.communicate(password.encode())
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)