OSError:subprocess中的参数无效

Python 3.3.3 Windows 7

Here is the full stack: Traceback (most recent call last): File "Blah\MyScript.py", line 578, in Call output = process.communicate( input=SPACE_KEY, timeout=600 ) File "C:\Python33\lib\subprocess.py", line 928, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "C:\Python33\lib\subprocess.py", line 1202, in _communicate self.stdin.write(input) OSError: [Errno 22] Invalid argument 

代码如下所示:

 process = subprocess.Popen( arguments, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, env=environment ) output = process.communicate( input=SPACE_KEY, timeout=600 ) 

此代码每天运行数百次,没有任何问题。 但是,如果多个脚本在同一台机器上运行(相同的脚本,但有时来自不同的文件夹),我得到的错误。 脚本不执行相同的事情(即:当我得到这个错误时,另一个脚本不执行一个subprocess)。

subProcess代码引发了许多不同的命令行input错误。

那么,有谁知道发生了什么? 解释器是否有多个执行的问题(在不同的进程中)? 如果解释器运行的是相同的(或者非常相似的)脚本,那么相同的代码通常可以很好地工作。 但是他们通常执行脚本的不同部分。

我不知所措:在8核心机器上使用单个处理器是令人讨厌的。

Solutions Collecting From Web of "OSError:subprocess中的参数无效"

@eryksun很好地分析了这个问题的核心,但是我认为他的答案中缺少更大的图像(可能是很明显的东西)。

那么,有谁知道发生了什么?

您的代码遭受竞争条件。 有时你的孩子的过程不像你认为的那样行为。

OSError: [Errno 22] Invalid argument在您的情况下,如果 communicate尝试写入命名管道之前退出,那么您的子进程的父级和标准输入之间建立了哪个subprocess进程,将引发OSError: [Errno 22] Invalid argument

Popen()在引擎盖下做了很多。 在你的情况下,它首先通过_winapi.CreatePipe()_get_handles()创建三个命名管道,每个stdin / out / err一个。 然后,它使用_winapi.CreateProcess()生成子进程(在_execute_child() _winapi.CreateProcess()

_execute_child()完成清理过程。 记住:所有这一切都发生在Popen()

只有在Popen()返回后,父进程中的Python VM才会继续调用output = process.communicate(input=SPACE_KEY, timeout=600)

考虑到你是在一个多核系统上,你的系统有时间片可以让你的Python解释器仍然执行Popen()时候让子进程做一些工作。

也就是说, _winapi.CreateProcess()之间有一个窄的时间窗口 (在子进程完成一些工作之后)以及Python试图通过communicate() (这会调用Windows的WriteFile ,作为eryksun很好地解释)。

当你的孩子在那个时间窗口退出,你检索命名错误。

为什么你的孩子的过程比预期的更早退出? 只有你可以告诉。 显然,它并不总是等待来自stdin的数据。

在写入流程stdin时,以前的communicate只会忽略一个EPIPE错误。 如果孩子已经退出(参见Lib / subprocess.py行1199),那么从每个问题19612的 3.3.5开始,它也忽略EINVAL (22)。

背景:

process.communiciate调用process.stdin.write ,它调用io.FileIO.write ,它在Windows上调用C运行时_write ,它调用Win32 WriteFile (在这种情况下调用NtWriteFile ,调度到NamedPipe文件系统,作为IRP_MJ_WRITEFastIoWrite )。

如果后者失败,它会在线程中设置一个Windows 系统错误代码 。 在这种情况下,底层的Windows错误可能是ERROR_NO_DATA (232),因为子进程已经退出。 C运行时将其映射到EINVAL (22)的errno值。 然后,由于_write失败, FileIO.write会根据errno的当前值引发OSError


附录:

如果CRT将ERROR_NO_DATA映射到EPIPE则根本不会有问题。 Python自己的Windows错误转换通常遵循CRT,但是在每个问题13063中 ,映射ERROR_NO_DATAEPIPE (32)是个例外。 因此,如果孩子已经退出, _winapi.WriteFile引发BrokenPipeError

下面的示例在子进程已经退出的情况下复制EINVAL错误。 它还显示_winapi.WriteFile (3.3.3源链接)将如何将此错误映射到EPIPE 。 国际海事组织,这应该被视为微软的CRT中的一个错误。

 >>> cmd = 'reg query hkcu' >>> process = Popen(cmd, stdin=PIPE, stdout=PIPE, universal_newlines=True) >>> process.stdin.write(' ') Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 22] Invalid argument >>> hstdin = msvcrt.get_osfhandle(process.stdin.fileno()) >>> _winapi.WriteFile(hstdin, b' ') Traceback (most recent call last): File "<stdin>", line 1, in <module> BrokenPipeError: [WinError 232] The pipe is being closed 

对于您正在使用的操作系统,该命令(args)可能不正确。 尝试消毒(检查/只通过允许的字符)。