python - Executable called via subprocess.check_output prints on console but result is not returned -
on windows machine, i'm trying call external executable python , gather outputs further processing. because local path variable has set before calling executable, created batch script
- first calls script set %path% ,
- then calls executable parameters given it.
the *.bat file looks this:
@echo off call set_path.bat @echo on executable.exe %*
and python code 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, works. executable called intended parameters, runs, finishes , produces results. know this, because mockingly displayed in console in python script running.
however, result string contains batch script returns, not executables outputs:
before call
hello? yes, executable.exe
after call
------- ------- ------- printing result ------- ------- -------
c:\users\me\documents\pythonscript\execute\executable.exe "para1|para2|para3"
------- ------- ------- /printing result ------- ------- -------
the subprocess.check_output command somehow prints intended output console, returns contains batch file's outputs after @echo on again.
how can access , save executable's output string further work?
or have somehow modify batch file catch , print output, end upt in check_output's results? if so, how go doing that?
if program writes directly console (e.g. opening conout$
device) instead of process standard handles, option read console screen buffer directly. make simpler, start new, empty screen buffer. create, size, initialize, , activate new screen buffer via following functions:
createconsolescreenbuffer
getconsolescreenbufferinfoex
(minimum supported client: windows vista)setconsolescreenbufferinfoex
(minimum supported client: windows vista)setconsolewindowinfo
fillconsoleoutputcharacter
setconsoleactivescreenbuffer
make sure request generic_read | generic_write
access when calling createconsolescreenbuffer
. you'll need read access later in order read contents of screen.
specifically python, use ctypes call functions in windows console api. also, if wrap handle c file descriptor via msvcrt.open_osfhandle
, can pass stdout
or stderr
argument of subprocess.popen
.
the file descriptor or handle screen buffer can't read directly via read
, readfile
, or readconsole
. if have file descriptor, underlying handle via msvcrt.get_osfhandle
. given screen buffer handle, call readconsoleoutputcharacter
read screen. read_screen
function in sample code below demonstrates reading beginning of screen buffer cursor position.
a process needs attached console in order use console api. end, i've included simple allocate_console
context manager temporarily open console. useful in gui application, isn't attached console.
the following example tested in windows 7 , 10, in python 2.7 , 3.5.
ctypes definitions
import os import contextlib import msvcrt import ctypes ctypes import wintypes kernel32 = ctypes.windll('kernel32', use_last_error=true) generic_read = 0x80000000 generic_write = 0x40000000 file_share_read = 1 file_share_write = 2 console_textmode_buffer = 1 invalid_handle_value = wintypes.handle(-1).value std_output_handle = wintypes.dword(-11) std_error_handle = wintypes.dword(-12) def _check_zero(result, func, args): if not result: raise ctypes.winerror(ctypes.get_last_error()) return args def _check_invalid(result, func, args): if result == invalid_handle_value: raise ctypes.winerror(ctypes.get_last_error()) return args if not hasattr(wintypes, 'lpdword'): # python 2 wintypes.lpdword = ctypes.pointer(wintypes.dword) wintypes.psmall_rect = ctypes.pointer(wintypes.small_rect) class coord(ctypes.structure): _fields_ = (('x', wintypes.short), ('y', wintypes.short)) class console_screen_buffer_infoex(ctypes.structure): _fields_ = (('cbsize', wintypes.ulong), ('dwsize', coord), ('dwcursorposition', coord), ('wattributes', wintypes.word), ('srwindow', wintypes.small_rect), ('dwmaximumwindowsize', coord), ('wpopupattributes', wintypes.word), ('bfullscreensupported', wintypes.bool), ('colortable', wintypes.dword * 16)) def __init__(self, *args, **kwds): super(console_screen_buffer_infoex, self).__init__( *args, **kwds) self.cbsize = ctypes.sizeof(self) pconsole_screen_buffer_infoex = ctypes.pointer( console_screen_buffer_infoex) lpsecurity_attributes = wintypes.lpvoid kernel32.getstdhandle.errcheck = _check_invalid kernel32.getstdhandle.restype = wintypes.handle kernel32.getstdhandle.argtypes = ( wintypes.dword,) # _in_ nstdhandle kernel32.createconsolescreenbuffer.errcheck = _check_invalid kernel32.createconsolescreenbuffer.restype = wintypes.handle kernel32.createconsolescreenbuffer.argtypes = ( wintypes.dword, # _in_ dwdesiredaccess wintypes.dword, # _in_ dwsharemode lpsecurity_attributes, # _in_opt_ lpsecurityattributes wintypes.dword, # _in_ dwflags wintypes.lpvoid) # _reserved_ lpscreenbufferdata kernel32.getconsolescreenbufferinfoex.errcheck = _check_zero kernel32.getconsolescreenbufferinfoex.argtypes = ( wintypes.handle, # _in_ hconsoleoutput pconsole_screen_buffer_infoex) # _out_ lpconsolescreenbufferinfo kernel32.setconsolescreenbufferinfoex.errcheck = _check_zero kernel32.setconsolescreenbufferinfoex.argtypes = ( wintypes.handle, # _in_ hconsoleoutput pconsole_screen_buffer_infoex) # _in_ lpconsolescreenbufferinfo kernel32.setconsolewindowinfo.errcheck = _check_zero kernel32.setconsolewindowinfo.argtypes = ( wintypes.handle, # _in_ hconsoleoutput wintypes.bool, # _in_ babsolute wintypes.psmall_rect) # _in_ lpconsolewindow kernel32.fillconsoleoutputcharacterw.errcheck = _check_zero kernel32.fillconsoleoutputcharacterw.argtypes = ( wintypes.handle, # _in_ hconsoleoutput wintypes.wchar, # _in_ ccharacter wintypes.dword, # _in_ nlength coord, # _in_ dwwritecoord wintypes.lpdword) # _out_ lpnumberofcharswritten kernel32.readconsoleoutputcharacterw.errcheck = _check_zero kernel32.readconsoleoutputcharacterw.argtypes = ( wintypes.handle, # _in_ hconsoleoutput wintypes.lpwstr, # _out_ lpcharacter wintypes.dword, # _in_ nlength coord, # _in_ dwreadcoord wintypes.lpdword) # _out_ lpnumberofcharsread
functions
@contextlib.contextmanager def allocate_console(): allocated = kernel32.allocconsole() try: yield allocated finally: if allocated: kernel32.freeconsole() @contextlib.contextmanager def console_screen(ncols=none, nrows=none): info = console_screen_buffer_infoex() new_info = console_screen_buffer_infoex() nwritten = (wintypes.dword * 1)() hstdout = kernel32.getstdhandle(std_output_handle) kernel32.getconsolescreenbufferinfoex( hstdout, ctypes.byref(info)) if ncols none: ncols = info.dwsize.x if nrows none: nrows = info.dwsize.y elif nrows > 9999: raise valueerror('nrows must 9999 or less') fd_screen = none hscreen = kernel32.createconsolescreenbuffer( generic_read | generic_write, file_share_read | file_share_write, none, console_textmode_buffer, none) try: fd_screen = msvcrt.open_osfhandle( hscreen, os.o_rdwr | os.o_binary) kernel32.getconsolescreenbufferinfoex( hscreen, ctypes.byref(new_info)) new_info.dwsize = coord(ncols, nrows) new_info.srwindow = wintypes.small_rect( left=0, top=0, right=(ncols - 1), bottom=(info.srwindow.bottom - info.srwindow.top)) kernel32.setconsolescreenbufferinfoex( hscreen, ctypes.byref(new_info)) kernel32.setconsolewindowinfo(hscreen, true, ctypes.byref(new_info.srwindow)) kernel32.fillconsoleoutputcharacterw( hscreen, u'\0', ncols * nrows, coord(0,0), nwritten) kernel32.setconsoleactivescreenbuffer(hscreen) try: yield fd_screen finally: kernel32.setconsolescreenbufferinfoex( hstdout, ctypes.byref(info)) kernel32.setconsolewindowinfo(hstdout, true, ctypes.byref(info.srwindow)) kernel32.setconsoleactivescreenbuffer(hstdout) finally: if fd_screen not none: os.close(fd_screen) else: kernel32.closehandle(hscreen) def read_screen(fd): hscreen = msvcrt.get_osfhandle(fd) csbi = console_screen_buffer_infoex() kernel32.getconsolescreenbufferinfoex( hscreen, ctypes.byref(csbi)) ncols = csbi.dwsize.x pos = csbi.dwcursorposition length = ncols * pos.y + pos.x + 1 buf = (ctypes.c_wchar * length)() n = (wintypes.dword * 1)() kernel32.readconsoleoutputcharacterw( hscreen, buf, length, coord(0,0), n) lines = [buf[i:i+ncols].rstrip(u'\0') in range(0, n[0], ncols)] return u'\n'.join(lines)
example
if __name__ == '__main__': import io import textwrap import subprocess text = textwrap.dedent('''\ lorem ipsum dolor sit amet, consectetur adipiscing elit, sed eiusmod tempor incididunt ut labore et dolore magna aliqua. ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.''') cmd = ("python -c \"" "print('piped output');" "conout = open(r'conout$', 'w');" "conout.write('''%s''')\"" % text) allocate_console() allocated: console_screen(nrows=1000) fd_conout: stdout = subprocess.check_output(cmd).decode() conout = read_screen(fd_conout) io.open('result.txt', 'w', encoding='utf-8') f: f.write(u'stdout:\n' + stdout) f.write(u'\nconout:\n' + conout)
output
stdout: piped output conout: lorem ipsum dolor sit amet, consectetur adipiscing elit, sed eiusmod tempor incididunt ut labore et dolore magna aliqua. ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Comments
Post a Comment