Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python select()未在forkpty()之后等待终端输入_Python_Linux - Fatal编程技术网

Python select()未在forkpty()之后等待终端输入

Python select()未在forkpty()之后等待终端输入,python,linux,Python,Linux,我正在尝试编写一个python脚本,它将通过ssh自动登录到远程主机并更新用户密码。由于ssh要求它从终端获取输入,因此我使用os.forkpty(),在子进程中运行ssh,并使用父进程通过伪终端发送命令输入。以下是我到目前为止的情况: import os, sys, time, getpass, select, termios # Time in seconds between commands sent to tty SLEEP_TIME = 1 NETWORK_TIMEOUT = 15

我正在尝试编写一个python脚本,它将通过ssh自动登录到远程主机并更新用户密码。由于ssh要求它从终端获取输入,因此我使用os.forkpty(),在子进程中运行ssh,并使用父进程通过伪终端发送命令输入。以下是我到目前为止的情况:

import os, sys, time, getpass, select, termios

# Time in seconds between commands sent to tty
SLEEP_TIME = 1
NETWORK_TIMEOUT = 15

#------------------------------Get Passwords------------------------------------

# get username
login = getpass.getuser()

# get current password
current_pass = getpass.getpass("Enter current password: ")

# get new password, retry if same as current password
new_pass = current_pass
first_try = True

while new_pass == current_pass:
    if first_try:
        first_try = False
    else:
        # New password equal to old password
        print("New password must differ from current password.")

    # Get new password
    new_pass = getpass.getpass("Enter new password: ")
    new_pass_confirm = getpass.getpass("Confirm new password: ")
    while new_pass != new_pass_confirm:
        # Passwords do not match
        print("Passwords do not match")
        new_pass = getpass.getpass("Enter new password: ")
        new_pass_confirm = getpass.getpass("Confirm new password: ")

#------------------------------End Get Passwords--------------------------------

ssh = "/usr/bin/ssh" # ssh bin location
args = ["ssh", login + "@localhost", "-o StrictHostKeyChecking=no"]

#fork
pid, master = os.forkpty()
if pid == 0:
    # Turn off echo so master does not need to read back its own input
    attrs = termios.tcgetattr(sys.stdin.fileno())
    attrs[3] = attrs[3] & ~termios.ECHO
    termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attrs)
    os.execv(ssh, args)
else:
    select.select([master], [], [])
    os.write(master, current_pass + "\n")
    select.select([master], [], [])
    os.write(master, "passwd\n")
    select.select([master], [], [])
    os.write(master, current_pass + "\n")
    select.select([master], [], [])
    os.write(master, new_pass + "\n")
    select.select([master], [], [])
    os.write(master, new_pass + "\n")
    select.select([master], [], [])
    os.write(master, "id\n")
    select.select([master], [], [])
    sys.stdout.write(os.read(master, 2048))
    os.wait()
该脚本提示用户输入他/她的当前密码和新密码,然后分叉并向ssh登录提示和passwd提示发送适当的响应

我遇到的问题是select系统调用的行为与我预期的不一样。他们看起来一点也不阻塞。我认为我对select与pty主端的工作方式有误解

如果我用time.sleep(1)替换它们,脚本工作得很好,但我不想依赖该解决方案,因为我不能总是保证网络会在短时间内响应,我也不想让它成为一件需要花费很长时间的奇迹(我打算使用它以编程方式登录多个服务器以更新密码)

有没有一种方法可以可靠地轮询pty的主端以等待从机的输出


注意:我知道sshpass和chpasswd之类的东西可以更好地解决这个问题,但我所处的环境无法以root用户身份运行,而且可用的实用程序很少。谢天谢地,python是这样的。

请查看
-f
选项中的
manssh
。这可能是启动
ssh
时需要的>。它将阻止
ssh
,直到键入密码,然后自行分叉。您可能可以使用此功能来实现所需的功能(但您可能需要稍微更改当前代码,因为它将自行执行您当前试图嵌入脚本中的操作)


此选项通常用于启动远程图形程序的命令行:一旦键入密码,您就可以安全地关闭终端,并通过其图形界面与远程进程保持交互。但我认为在这里使用此功能将比使用低级阻塞功能和类似的事情。

选择
不读取任何数据;它只是阻塞,直到可以读取数据

由于在第一次
select
之后没有读取任何数据,因此缓冲区中仍有数据可供读取,因此任何后续的
select
都不会阻塞

在再次调用
select
之前,您需要读取缓冲区中的数据。在不阻塞的情况下执行此操作意味着您可能必须将文件设置为非阻塞模式(我不知道如何在Python中执行此操作)


通过SSH提供密码的更好方法是,直接通过SSH而不是通过创建的shell运行命令

handle = subprocess.Popen(["ssh", login + "@localhost", "-o StrictHostKeyChecking=no", "passwd --stdin"])
handle.communicate("\n".join([oldpass, newpass, newpass, ""]))

这是我的问题的一部分。从文件中读取当前数据可以解决部分问题。现在的问题是确定ssh何时完成写入。例如,在第一次选择之后,我将读取一半的登录消息。不幸的是,我的passwd不支持--stdin标志。该方法也无法解决输入初始值的需要ssh的密码。