如何“更换"Windows 上的 `os.execvpe` 有效 - 如果“子"进程是一个交互式命令行应用程序吗? [英] How to "replace" `os.execvpe` on Windows efficiently - if the "child" process is an interactive command line application?

查看:37
本文介绍了如何“更换"Windows 上的 `os.execvpe` 有效 - 如果“子"进程是一个交互式命令行应用程序吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个 Python 脚本,经过一些准备工作后,它会启动 ssh.我的脚本实际上是一个小的 CLI 工具.在类 Unix 系统上,在其生命周期结束时,Python 脚本将自身替换为 ssh 客户端,因此用户现在可以直接与 ssh 交互(即运行任意远程机器上的命令等):

I am dealing with a Python script which does, after some preparation work, launch ssh. My script is actually a small CLI tool. On Unix-like systems, at the end of its life, the Python script replaces itself with the ssh client, so the user can now interact with ssh directly (i.e. run arbitrary commands on the remote machine etc):

os.execvpe('ssh', ['ssh', '-o', 'foo', 'user@host'], os.environ)

积极的惊喜 &旁注,以防您想知道:Windows 10 实际上内置了 OpenSSH 的本机版本,因此该平台上有一个 ssh 命令.

Positive surprise & side-note in case you are wondering: Windows 10 actually has a native version of OpenSSH now built-in, so there is a ssh command on this platform.

os.execvpe 存在于 Windows 上的 Python 标准库中,但它不会替换原始 (Python) 进程.情况……有点复杂:123.底线:Windows 没有实现相应的 POSIX 语义来替换正在运行的进程.

os.execvpe is present in the Python standard library on Windows, but it does not replace the original (Python) process. The situation is ... somewhat complicated: 1, 2, 3. Bottom line: Windows does not implement the corresponding POSIX semantics for replacing a running process.

通常的做法是使用 subprocess.Popen 代替,可以有效地创建子进程.我可以启动子进程以便父进程继续运行,或者我可以在父进程死亡时启动子进程(我认为 Windows 确实像类 Unix 系统一样支持后者).无论哪种方式,用户都无法在命令行中与孩子进行交互.

The common wisdom is to use subprocess.Popen instead, ok, effectively creating a child process. I can launch the child so that the parent keeps running OR I can launch the child while the parent dies (I think that Windows does support the latter just like Unix-like systems). Either way, the user can not interact with the child in the command line.

假设我让父级保持活动状态,我现在必须编写大量代码来通过父级将用户 I/O 传递给/从子级传递,例如 例如.后者涉及管理流甚至线程,这取决于它应该表现得有多好 - 很多地方可能会出现潜在问题和故障.我不喜欢这样做(如果可以避免的话).

Assuming that I keep the parent alive, I now have to write a ton of code to pass user I/O to/from the child through the parent, like so for instance. The latter involves managing streams and even threads, depending on how well it is supposed to behave - a lot of places for potential issues and breakages down the road. I do not like to do this (if I can avoid it).

如何在所描述的场景中有效地替换 Windows 上的 os.execvpe?

How can I efficiently replace os.execvpe on Windows in the described scenario?

编辑(1):可能相关的点点滴滴......

EDIT (1): Bits and pieces, which may be relevant ...

我想这取决于在将 STARTUPINFO 对象传递给 Popen 之前弄清楚如何正确配置它.命令行实际上可以在 Windows 中继承.

I guess it depends on figuring out how to correctly configure a STARTUPINFO object before passing it into Popen. A command line can in fact be inherited in Windows.

EDIT (2):通过 pywin32 的部分解决方案 - ssh 打开第二个新的 cmd 窗口并可以与之交互.带有 Python 的原始 shell 保持打开状态,Python 本身退出:

EDIT (2): A partial solution via pywin32 - ssh opens into a second, new cmd window and can be interacted with. The original shell with Python remains open, Python itself quits:

from win32.Demos.winprocess import Process
from shlex import join
Process(join(['ssh', '-o', 'foo', 'user@host']))

推荐答案

一个部分不完整的解决方案如下,见TODO评论:

A partial and incomplete solution looks about as follows, see TODO comments:

import win32api, win32process, win32con
from shlex import join

si = win32process.STARTUPINFO()

# TODO fix flags
si.dwFlags = win32con.STARTF_USESTDHANDLES ^ win32con.STARTF_USESHOWWINDOW

# inherit stdin, stdout and stderr
si.hStdInput = win32api.GetStdHandle(win32api.STD_INPUT_HANDLE)
si.hStdOutput = win32api.GetStdHandle(win32api.STD_OUTPUT_HANDLE)
si.hStdError = win32api.GetStdHandle(win32api.STD_ERROR_HANDLE)

# TODO fix value?
si.wShowWindow = 1

# TODO set values?
# si.dwX, si.dwY = ...
# si.dwXSize, si.dwYSize = ...
# si.lpDesktop = ...

procArgs = (
    None,  # appName
    join(['ssh', '-o', 'foo', 'user@host']),  # commandLine
    None,  # processAttributes
    None,  # threadAttributes
    1,  # bInheritHandles TODO ?
    win32process.CREATE_NEW_CONSOLE,  # dwCreationFlags
    None,  # newEnvironment
    None,  # currentDirectory
    si, # startupinfo
)

procHandles = win32process.CreateProcess(*procArgs) # run ...

ssh 打开第二个新的 cmd.exe 窗口,可以与之交互.包含 Python 的原始 cmd.exe 窗口保持打开状态,Python 本身退出,将控制权返回给 cmd.exe 本身.它是可用的,虽然不一致和丑陋.

ssh opens into a second, new cmd.exe window and can be interacted with. The original cmd.exe window with Python in it remains open, Python itself quits, returning control to cmd.exe itself. It is usable, although inconsistent and ugly.

我想这归结为正确配置 win32process.STARTUPINFO,但即使在阅读大量文档之后,我还是无法理解它......

I guess it comes down to configuring win32process.STARTUPINFO correctly, but even after heaving read tons of documentation on it, I am somehow failing to make sense of it ...

这篇关于如何“更换"Windows 上的 `os.execvpe` 有效 - 如果“子"进程是一个交互式命令行应用程序吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆