Popen with Context Managers

Viewed 8753

I have been trying to write a function which would execute a command passed to it thru a parameter using POPEN along with Context Managers. Unfortunately, I am unable to get it to work. Can someone please help?

import os
import sys
import subprocess
import inspect    

def run_process(cmd_args):
    with subprocess.Popen(cmd_args, stdout=subprocess.PIPE) as proc:
        log.write(proc.stdout.read())

run_process("print('Hello')")

The output expected is "Hello". Can someone please point out where I am going wrong?

4 Answers

What you have done is right if you are running a bash command through the subprocess.

Inside the context manager "with ..." what you have done is to reading out the output from the terminal and storing them as byte(s) in "output" variable and trying to print out the bytes in ASCII after decoding it.

Try returning the value from the context manager and then decode it in the calling function:

import os
import sys
import subprocess
import inspect    

def run_process(cmd_args):          # Below added shell=True' in parameters.
    with subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=True) as proc: 
        return proc.stdout.read()  # returns the output 
                               # Optionally you can use the 'encoding='utf-8' argument 
                               # instead and just print(proc.stdout.read()).   
print(run_process().decode('utf-8')) 

I was having a similar issue while pipelining a process to another program and I did the decoding in the other program and surprisingly it worked. Hope it works for you as well.

def run_process(cmd_args):
    with subprocess.Popen(cmd_args, stdout=subprocess.PIPE) as p:    
        output = p.stdout.read() 
        return output

It worked for the same question.

Popen runs the command it receives as you would run something in your terminal (example: CMD on Windows or bash on Linux). So, it does not execute Python, but Bash code (on Linux for ex). The Python binary has a command, -c that does what you would need: executes a Python command right away. So you have to options:

  • either use echo Hello (works on Windows or Linux too, echo it's both in batch and in bash)
  • or you could use python -c "print('Hello') instead of just the print command.

Without making too many changes to your existing script, I have edited your script with the below comments indicating what I did to get it to work. I hope this helps.

import os
import sys
import subprocess
import inspect    

def run_process(cmd_args):          # Below added shell=True' in parameters.
    with subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=True) as proc: 
        output = proc.stdout.read()   # Reads the output from the process in bytes.
        print(output.decode('utf-8'))  # Converts bytes to UTF-8 format for readability. 
                                   # Optionally you can use the 'encoding='utf-8' argument 
                                   # instead and just print(proc.stdout.read()).   
run_process("echo Hello")  # To display the message in the prompt use 'echo' in your string like this.

Note: Read the Security Considerations section before using shell=True. https://docs.python.org/3/library/subprocess.html#security-considerations

Related