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