为什么当标准输出和标准输入被重定向时,Python的行为与预期不符?

3

我尝试在Windows中使用CreateProcess()重定向cmd.exe的stdout和stdin。只要我运行简单的命令或打开GUI应用程序,它就可以正常工作,但是如果我尝试运行像Python这样的软件,它就不再输出(也无法通过stdin获取输入)。

代码示例:

#include <windows.h> 
#include <iostream>
#include <string>
#include <thread>

using namespace std;

HANDLE child_input_read;
HANDLE child_input_write;
HANDLE child_output_read;
HANDLE child_output_write;

void writeToPipe()
{
 while (true)
 {
  DWORD bytes_written;
  string msg;
  getline(cin, msg);
  msg += '\n';
  BOOL success = WriteFile(child_input_write, msg.c_str(), msg.size(), &bytes_written, NULL);
  if (!success)
  {
   break;
  }
 }
}
void readFromPipe()
{
 while (true)
 {
  DWORD bytes_read;
  char buffer[512];
  BOOL success = ReadFile(child_output_read, buffer, sizeof(buffer)-1, &bytes_read, NULL);
  buffer[bytes_read] = 0;
  if (!success)
  {
   break;
  }
  cout << buffer;
 }
}
void createCmdProcess()
{
 PROCESS_INFORMATION process_info;
 STARTUPINFO startup_info;
 SECURITY_ATTRIBUTES security_attributes;

 // Set the security attributes for the pipe handles created 
 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
 security_attributes.bInheritHandle = TRUE;
 security_attributes.lpSecurityDescriptor = NULL;
 CreatePipe(&child_output_read, &child_output_write, &security_attributes, 0);
 CreatePipe(&child_input_read, &child_input_write, &security_attributes, 0);

 // Create the child process
 ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
 ZeroMemory(&startup_info, sizeof(STARTUPINFO));
 startup_info.cb = sizeof(STARTUPINFO);
 startup_info.hStdInput = child_input_read;
 startup_info.hStdOutput = child_output_write;
 startup_info.hStdError = child_output_write;
 startup_info.dwFlags |= STARTF_USESTDHANDLES;
 CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info);
}

int main()
{
 createCmdProcess();
 thread t(writeToPipe);
 thread t2(readFromPipe);
 t.join();
 t2.join();
 system("pause");
}


1
默认情况下,控制台进程会获得与其父进程相同的标准输入和标准输出。然而,没有办法强制一个进程使用它所拥有的标准输入和标准输出。我会感到惊讶如果Python在这方面表现不当,你确定你正在运行控制台版本吗?输出实际上在哪里结束了? - Harry Johnston
我确定我运行了cmd.exe,但是当我启动Python时没有任何输出。 - liran63
是的,我刚刚注意到并进行了编辑。那不是问题,我只是快速编写了一个示例代码。对此感到抱歉。 - liran63
我再次检查了一遍,问题仍然存在。我还在Windows 7机器上进行了检查,但问题依旧存在。 - liran63
1
好的,我想我明白你的意思了;cmd.exe可以工作,Python命令也可以,但是Python的交互模式不行。 - Harry Johnston
显示剩余3条评论
1个回答

3

这不是一个错误,而是一个特性。 :-)

来自Python设置和使用, 1.1.1节, 接口选项(强调添加):

解释器界面类似于UNIX shell,但提供了一些额外的调用方法:

当使用标准输入连接到tty设备时,它会提示命令并执行它们,直到读取到EOF(文件结束符,您可以在UNIX上使用Ctrl-D或在Windows上使用Ctrl-Z、Enter来产生它)。

当使用文件名参数或将文件作为标准输入时,它会从该文件读取并执行脚本。

管道既不是文件也不是tty设备,但在C标准库(因此Python)看来,它看起来像一个文件。因此第二种行为启动,并且Python尝试读取到文件结尾。由于我们从未关闭管道的一端,因此永远不会发生这种情况。

我认为这种行为并不特别明智(至少在Windows中是这样),如果您愿意,可以提交一个错误报告。我猜测这样的提议会被拒绝,因为任何更改都将破坏向后兼容性,但我可能是错误的。
您可以通过在命令行上提供-i选项来解决该问题:
python -i

这会让Python进入交互模式,尽管stdin不是终端。

不幸的是,在Windows上没有已知的方法可以创建一个看起来像终端的管道。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接