On a Windows machine, I'm trying to call an external executable from Python and gather its outputs for further processing. Because a local path variable has to be set before calling the executable, I created a batch script that
- first calls another script to set %PATH% and
- then calls the executable with the parameters given to it.
The *.bat file looks like this:
@echo off
call set_path.bat
@echo on
executable.exe %*
And the Python code like this:
print("before call");
result = subprocess.check_output([batfile, parameters], stderr=subprocess.STDOUT, shell=True);
print("after call");
print("------- ------- ------- printing result ------- ------- ------- ");
print(result);
print("------- ------- ------- /printing result ------- ------- ------- ");
Now, technically, this works. The executable is called with the intended parameters, runs, finishes and produces results. I know this, because they are mockingly displayed in the very console in which the Python script is running.
However, the result string only contains what the batch script returns, not the executables outputs:
before call
hello? yes, this is executable.exe
after call
------- ------- ------- printing result ------- ------- -------
C:\Users\me\Documents\pythonscript\execute\executable.exe "para1|para2|para3"
------- ------- ------- /printing result ------- ------- -------
The subprocess.check_output command itself somehow prints the intended output to the console, what it returns only contains the batch file's outputs after @echo is on again.
How can I access and save the executable's output to a string for further work?
Or do I have to somehow modify the batch file to catch and print the output, so that it will end upt in check_output's results? If so, how could I go about doing that?
If a program writes directly to the console (e.g. by opening the
CONOUT$
device) instead of to the process standard handles, the only option is to read the console screen buffer directly. To make this simpler, start with a new, empty screen buffer. Create, size, initialize, and activate a new screen buffer via the following functions:CreateConsoleScreenBuffer
GetConsoleScreenBufferInfoEx
(Minimum supported client: Windows Vista)SetConsoleScreenBufferInfoEx
(Minimum supported client: Windows Vista)SetConsoleWindowInfo
FillConsoleOutputCharacter
SetConsoleActiveScreenBuffer
Make sure to request
GENERIC_READ | GENERIC_WRITE
access when callingCreateConsoleScreenBuffer
. You'll need read access later in order to read the contents of the screen.Specifically for Python, use ctypes to call functions in the Windows console API. Also, if you wrap the handle with a C file descriptor via
msvcrt.open_osfhandle
, then you can pass it as thestdout
orstderr
argument ofsubprocess.Popen
.The file descriptor or handle for the screen buffer can't be read directly via
read
,ReadFile
, or evenReadConsole
. If you have a file descriptor, get the underlying handle viamsvcrt.get_osfhandle
. Given a screen buffer handle, callReadConsoleOutputCharacter
to read from the screen. Theread_screen
function in the sample code below demonstrates reading from the beginning of the screen buffer up to the cursor position.A process needs to be attached to a console in order to use the console API. To that end, I've included a simple
allocate_console
context manager to temporarily open a console. This is useful in a GUI application, which normally isn't attached to a console.The following example was tested in Windows 7 and 10, in Python 2.7 and 3.5.
ctypes definitions
functions
example
output