Reputation: 44094
On Windows, I'm running the following Python 3.5 script from cmd.exe
:
subprocess.run(['C:\\Program Files (x86)\\Putty\\plink.exe',
'root@server', '-P', '54022', '-i', 'key.ppk', 'exit'])
input('Press Enter...')
But when it's time to press Enter, the console is unresponsive. Enter does nothing. Text can't be entered. Ctrl+C doesn't do anything either. Python has to be killed in the task manager.
I suspect plink
is leaving the console in a bad state. Can I prevent or repair this? Or can I run the ssh command in its own console? That's not ideal, but it'll do.
Or maybe there's a better solution for running remote commands via SSH using Python?
When running the same plink
command directly from cmd
(no Python), it does remain responsive.
Upvotes: 1
Views: 691
Reputation: 34260
A process might modify the console state and then for some reason fail to restore the original state when it exits. If this is a problem, the easiest solution is to spawn the child process with its own console by adding the parameter creationflags=subprocess.CREATE_NEW_CONSOLE
.
If that's not an option, or at least not a preferred option, you could instead capture the current modes of the console's input and screen buffer prior to running the program. Then restore the previous modes after the child exits. See GetConsoleMode
and SetConsoleMode
on MSDN.
Here's a context manager to restore the console's input and output modes.
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)
This could be expanded to restore the input and output codepages via GetConsoleCP
, GetConsoleOutputCP
, SetConsoleCP
, and SetConsoleOutputCP
. It could also restore the screen dimensions, title, and so on. This is all global state in conhost.exe that a child process can meddle with. On the other hand, the console's input history and aliases are stored per attached executable, so you shouldn't have to restore them.
Upvotes: 2