我有一个子进程,要么退出返回码,要么问某些东西并等待用户输入。
我想检测进程何时提出问题并立即退出。过程提出问题的事实足以让我决定系统的状态。
问题在于我无法阅读问题,因为子进程可能不会刷新标准输出。所以我不能依赖解析subprocess.Popen().stdout
:当试图读取它时,它会因为首先读取输入而阻塞。
有点像这样
# ask.py, just asks something without printing anything if a condition is met
# here, we'll say that the condition is always met
input()
当然,实际的子进程是第三方二进制文件,我无法轻易修改它以添加必要的刷新调用,这将解决它。
我还可以尝试相当于unbuffer
(What is the equivalent of unbuffer program on Windows?)的Windows,它名为winpty
,它可能(可能)允许我检测输出并解决当前问题,但我我想保持简单,我想首先解决标准输入问题......
我试过......好吧,很多事情都行不通,包括尝试将假文件作为stdin
参数传递,这不起作用,因为subprocess
需要{{1}该文件,我们无法提供垃圾...
fileno
将p = subprocess.Popen(["python","ask.py"],...)
与字符串一起使用也不起作用,因为您无法控制何时读取字符串以供应给子进程(可能通过系统管道)。
这些问题很有希望,但要么依赖标准输出,要么只适用于Linux
我目前正在做的是在超时时运行进程,如果达到超时,则我决定程序被阻止。但它花费了超时等待时间。如果我能在子进程读取communicate
后立即做出决定,那就更好了。
我想知道是否存在本机python解决方案(可能使用stdin
和Windows扩展)来检测stdin的读取。但是,本机解决方案不使用Python而是使用非Microsoft专有语言。
答案 0 :(得分:2)
我想知道子进程是否读取用户输入是为了(ab)使用文件对象是有状态的这一事实:如果进程从其stdin读取数据,我们应该能够检测到stdin状态的变化。
程序如下:
tell()
方法查明是否从文件中读取了任何内容这是代码:
import os
import time
import tempfile
import subprocess
# create a file that we can use as the stdin for the subprocess
with tempfile.TemporaryFile() as proc_stdin:
# write some data to the file for the subprocess to read
proc_stdin.write(b'whatever\r\n')
proc_stdin.seek(0)
# start the thing
cmd = ["python","ask.py"]
proc = subprocess.Popen(cmd, stdin=proc_stdin, stdout=subprocess.PIPE)
# wait for it to start up and do its thing
time.sleep(1)
# now check if the subprocess read any data from the file
if proc_stdin.tell() == 0:
print("it didn't take input")
else:
print("it took input")
理想情况下,临时文件可能会被某种管道或不能将任何数据写入磁盘的东西所取代,但遗憾的是,如果没有真正的磁盘文件,我找不到让它工作的方法。
答案 1 :(得分:2)
如果我们不想让子进程处理用户输入,但在这种情况下简单地杀死它,解决方案可以是下一步:
c ++上的一种可能的实现:
struct ReadWriteContext : public OVERLAPPED
{
enum OpType : char { e_write, e_read } _op;
BOOLEAN _bCompleted;
ReadWriteContext(OpType op) : _op(op), _bCompleted(false)
{
}
};
VOID WINAPI OnReadWrite(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* lpOverlapped)
{
static_cast<ReadWriteContext*>(lpOverlapped)->_bCompleted = TRUE;
DbgPrint("%u:%x %p\n", static_cast<ReadWriteContext*>(lpOverlapped)->_op, dwErrorCode, dwNumberOfBytesTransfered);
}
void nul(PCWSTR lpApplicationName)
{
ReadWriteContext wc(ReadWriteContext::e_write), rc(ReadWriteContext::e_read);
static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si)};
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE != si.hStdInput)
{
char buf[256];
if (WriteFileEx(hPipe, "\n", 1, &wc, OnReadWrite))
{
si.hStdError = si.hStdOutput = si.hStdInput;
if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
BOOLEAN bQuit = true;
goto __read;
do
{
bQuit = true;
switch (WaitForSingleObjectEx(pi.hProcess, INFINITE, TRUE))
{
case WAIT_OBJECT_0:
DbgPrint("child terminated\n");
break;
case WAIT_IO_COMPLETION:
if (wc._bCompleted)
{
DbgPrint("child read from hStdInput!\n");
TerminateProcess(pi.hProcess, 0);
}
else if (rc._bCompleted)
{
__read:
rc._bCompleted = false;
if (ReadFileEx(hPipe, buf, sizeof(buf), &rc, OnReadWrite))
{
bQuit = false;
}
}
break;
default:
__debugbreak();
}
} while (!bQuit);
CloseHandle(pi.hProcess);
}
}
CloseHandle(si.hStdInput);
// let execute pending apc
SleepEx(0, TRUE);
}
CloseHandle(hPipe);
}
}
另一种代码变体 - 使用事件完成,而不是apc。但这不会影响最终结果。这个代码变体给出了与第一个完全相同的结果:
void nul(PCWSTR lpApplicationName)
{
OVERLAPPED ovw = {}, ovr = {};
if (ovr.hEvent = CreateEvent(0, 0, 0, 0))
{
if (ovw.hEvent = CreateEvent(0, 0, 0, 0))
{
static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si)};
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE != si.hStdInput)
{
char buf[256];
if (!WriteFile(hPipe, "\n", 1, 0, &ovw) && GetLastError() == ERROR_IO_PENDING)
{
si.hStdError = si.hStdOutput = si.hStdInput;
if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
BOOLEAN bQuit = true;
HANDLE h[] = { ovr.hEvent, ovw.hEvent, pi.hProcess };
goto __read;
do
{
bQuit = true;
switch (WaitForMultipleObjects(3, h, false, INFINITE))
{
case WAIT_OBJECT_0 + 0://read completed
__read:
if (ReadFile(hPipe, buf, sizeof(buf), 0, &ovr) || GetLastError() == ERROR_IO_PENDING)
{
bQuit = false;
}
break;
case WAIT_OBJECT_0 + 1://write completed
DbgPrint("child read from hStdInput!\n");
TerminateProcess(pi.hProcess, 0);
break;
case WAIT_OBJECT_0 + 2://process terminated
DbgPrint("child terminated\n");
break;
default:
__debugbreak();
}
} while (!bQuit);
CloseHandle(pi.hProcess);
}
}
CloseHandle(si.hStdInput);
}
CloseHandle(hPipe);
// all pending operation completed here.
}
CloseHandle(ovw.hEvent);
}
CloseHandle(ovr.hEvent);
}
}