Why doesn't Python behave as expected when stdout and stdin are redirected?

I am trying to redirect to windows cmd.exe stdout & stdin (using CreateProcess ()). It works fine as long as I run simple commands or open GUI applications, but if I try to run software like python, it no longer gives me its output (nor does it receive input via stdin).

Sample code:

#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");
}
      

Run codeHide result


+3


source to share


1 answer


It's not a bug, it's a feature. :-)

From Configuring and Using Python, section 1.1.1, Interface Options (emphasis mine):

The interpreter interface is similar to the UNIX shell interface, but provides some additional invocation methods:

When called with stdin connected to a tty device , it prompts for commands and executes them until EOF (end-of-file character, you can create this with Ctrl-D on UNIX or Ctrl-Z, Enter on Windows).

When called with a file name argument, or with a file as standard input, it reads and executes a script from that file.

The pipe is neither a file nor a tty device, but it looks like a file in the standard C library (and therefore Python). So the second behavior is triggered and Python tries to read to the end of the file. Since we never close our end of the pipe, this never happens.

This behavior is not particularly sensible in my opinion (or at least not on Windows) and you can file an error if you like. I would assume that such a proposal would be rejected as any change would break compatibility, but I could be wrong.



You can work around the problem by providing the command line parameter -i

:

python -i

      

This makes Python enter interactive mode even though stdin is not a terminal.

Unfortunately, there is no known way to make a pipe that looks like a terminal on Windows.

+3


source







All Articles