Python plink 后 input() 无响应

Python input() unresponsive after plink

在 Windows,我正在 运行 宁以下 Python 3.5 脚本来自 cmd.exe

subprocess.run(['C:\Program Files (x86)\Putty\plink.exe', 
                'root@server', '-P', '54022', '-i', 'key.ppk', 'exit'])     
input('Press Enter...')

但是当需要按 Enter 时,控制台没有响应。 Enter 什么都不做。无法输入文字。 Ctrl+C 也没有做任何事情。 Python 必须在任务管理器中杀死。

我怀疑 plink 使控制台处于不良状态。我可以防止或修复此问题吗?或者我可以 运行 在它自己的控制台中使用 ssh 命令吗?这并不理想,但它会做到的。

或者也许有更好的解决方案 运行 使用 Python 通过 SSH 执行远程命令?

当 运行直接从 cmd 执行相同的 plink 命令时(没有 Python),它会保持响应。

进程可能会修改控制台状态,然后由于某种原因在退出时无法恢复原始状态。如果这是一个问题,最简单的解决方案是通过添加参数 creationflags=subprocess.CREATE_NEW_CONSOLE 来使用自己的控制台生成 child 进程。

如果这不是一个选项,或者至少不是一个首选选项,您可以改为在 运行 程序之前捕获控制台输入和屏幕缓冲区的当前模式。然后在 child 退出后恢复以前的模式。请参阅 MSDN 上的 GetConsoleMode and SetConsoleMode

这里有一个上下文管理器,用于恢复控制台的输入和输出模式。

import ctypes
import msvcrt
import contextlib

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

def _check_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

kernel32.GetConsoleMode.errcheck = _check_bool
kernel32.SetConsoleMode.errcheck = _check_bool

@contextlib.contextmanager
def restore_console():
    if not kernel32.GetConsoleWindow():
        yield  # nothing to do
        return
    with open(r'\.\CONIN$', 'r+') as coni:
        with open(r'\.\CONOUT$', 'r+') as cono:
            hI = msvcrt.get_osfhandle(coni.fileno())
            hO = msvcrt.get_osfhandle(cono.fileno())
            imode = ctypes.c_ulong()
            omode = ctypes.c_ulong()
            kernel32.GetConsoleMode(hI, ctypes.byref(imode))
            kernel32.GetConsoleMode(hO, ctypes.byref(omode))
            yield
            try:
                kernel32.SetConsoleMode(hI, imode)
            finally:
                kernel32.SetConsoleMode(hO, omode)

这可以通过 GetConsoleCPGetConsoleOutputCPSetConsoleCPSetConsoleOutputCP 扩展以恢复输入和输出代码页。它还可以恢复屏幕尺寸、标题等。这是 conhost.exe 中的所有全局状态,child 进程可以干预。另一方面,控制台的输入历史记录和别名是按附加的可执行文件存储的,因此您不必恢复它们。