I am looking for the way to call shell scripts from python and write their stdout and stderr to file using logging. Here is my code:
import logging import tempfile import shlex import os def run_shell_command(command_line): command_line_args = shlex.split(command_line) logging.info('Subprocess: \"' + command_line + '\"') process_succeeded = True try: process_output_filename = tempfile.mktemp(suffix = 'subprocess_tmp_file_') process_output = open(process_output_filename, 'w') command_line_process = subprocess.Popen(command_line_args,\ stdout = process_output,\ stderr = process_output) command_line_process.wait() process_output.close() process_output = open(process_output_filename, 'r') log_subprocess_output(process_output) process_output.close() os.remove(process_output_filename) except: exception = sys.exc_info()[1] logging.info('Exception occured: ' + str(exception)) process_succeeded = False if process_succeeded: logging.info('Subprocess finished') else: logging.info('Subprocess failed') return process_succeeded
And I am sure that there is the way to do it without creating temporary file to store process output. Any ideas?
To constantly print Subprocess output while process is running with Python, we can loop through stdout and call print in the loop. to call Popen with the cmd command we want to run. Then we have a for loop that loops through p. stdout to get the output lines.
If you simply want to capture both STDOUT and STDERR independently, AND you are on Python >= 3.7, use capture_output=True .
subprocess. check_output() is the one that runs the command and returns the return value. If you want the output write your value to STDOUT and use check_output() to get the value.
The main difference is that subprocess. run() executes a command and waits for it to finish, while with subprocess. Popen you can continue doing your stuff while the process finishes and then just repeatedly call Popen.
You could try to pass the pipe directly without buffering the whole subprocess output in memory:
from subprocess import Popen, PIPE, STDOUT process = Popen(command_line_args, stdout=PIPE, stderr=STDOUT) with process.stdout: log_subprocess_output(process.stdout) exitcode = process.wait() # 0 means success
where log_subprocess_output()
could look like:
def log_subprocess_output(pipe): for line in iter(pipe.readline, b''): # b'\n'-separated lines logging.info('got line from subprocess: %r', line)
I am sure that there is the way to do it without creating temporary file to store process output
You simply have to check for the documentation of Popen
, in particular about stdout
and stderr
:
stdin
,stdout
andstderr
specify the executed program’s standard input, standard output and standard error file handles, respectively. Valid values arePIPE
, an existing file descriptor (a positive integer), an existing file object, andNone
.PIPE
indicates that a new pipe to the child should be created. With the default settings ofNone
, no redirection will occur; the child’s file handles will be inherited from the parent. Additionally,stderr
can beSTDOUT
, which indicates that thestderr
data from the child process should be captured into the same file handle as forstdout
.
So you can see that you can either use a file object, or the PIPE
value. This allows you to use the communicate()
method to retrieve the output:
from StringIO import StringIO process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, error = process.communicate() log_subprocess_output(StringIO(output))
I'd rewrite your code as:
import shlex import logging import subprocess from StringIO import StringIO def run_shell_command(command_line): command_line_args = shlex.split(command_line) logging.info('Subprocess: "' + command_line + '"') try: command_line_process = subprocess.Popen( command_line_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) process_output, _ = command_line_process.communicate() # process_output is now a string, not a file, # you may want to do: # process_output = StringIO(process_output) log_subprocess_output(process_output) except (OSError, CalledProcessError) as exception: logging.info('Exception occured: ' + str(exception)) logging.info('Subprocess failed') return False else: # no exception was raised logging.info('Subprocess finished') return True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With