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,stdoutandstderrspecify 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.PIPEindicates 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,stderrcan beSTDOUT, which indicates that thestderrdata 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