I'm trying to run a process with subprocess and print its entire output if and only if an exception occurs.
Where I was before:
try:
proc = subprocess.run(
command,
capture_output=True,
check=True,
text=True,
)
except subprocess.CalledProcessError as error:
print(error.output)
This did not work.
Output when subprocess.CalledProcessError occurs:
b''
Replacing capture_output with stdout=subprocess.PIPE resulted in the output of everything regardless whether an exception occurred or not, error.output was still empty.
So I experimented:
This prints everything I would see if I executed the command in the command-line.
subprocess.run(
command,
stdout=subprocess.PIPE,
)
This prints out nothing.
proc = subprocess.run(
command,
capture_output=True,
)
print(proc.stdout.decode())
I also tried subprocess.check_output() which to my information does the same as subprocess.run() with the flags I set in the first code snippet.
What am I missing here? Thanks.
Addendum
import subprocess
command = ['pandoc', 'file']
try:
proc = subprocess.run(
command,
capture_output=True,
check=True,
)
except subprocess.CalledProcessError as error:
print('Exception:')
print(error.output)
This is an MWE with the specific process I want to run (pandoc)
Output
$ pandoc file
pandoc: file: openBinaryFile: does not exist (No such file or directory)
$ ./samplecode.py
Exception:
b''
So the exception gets triggered, but the output object is empty.
It seems that the error message is present in error.stderr and not in error.output. I tried your example (with a ls of non-existent file) :
import subprocess
command = ['ls', 'file']
try:
proc = subprocess.run(
command,
check=True,
capture_output=True,
text=True
)
except subprocess.CalledProcessError as error:
print('Exception:')
print('output : ' + error.output)
print('stderr : ' + error.stderr)
The output is the following :
Exception:
output :
stderr : ls: file: No such file or directory
Hope it helps.
I believe what you're meaning to run is stderr=subprocess.PIPE. This should print the relevant error code to the standard console error output.
Example:
process = subprocess.Popen(['ls', 'myfile.txt'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output,error) = process.communicate()
if error:
print error
Related
I have a NSIS script that accepts an argument ${appname} and creates an installer based on the given name, the nsis script is called from a python script, which also does other stuff. This is the code I am using for calling the NSIS script
def run_nsis_process(self,product_name,script, logger):
NSIS_PATH='C:/Program Files (x86)/NSIS'
try:
nsis_args = '/Dappname='+product_name+ ' '+ script
process_completed = subprocess.run(['makensis.exe', nsis_args], shell=True, cwd = NSIS_PATH, universal_newlines = True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True)
except subprocess.CalledProcessError as err:
logger.error('The makensis.exe subprocess existed with the following code {} \n The makensis.exe produced the following error text{}'.format(err.returncode, err.output))
else:
logger.info('Output from NSIS:\n{}'.format(process_completed.stdout))
This gives me an error that I am not using the correct makensis.exe arguments. Just for information the /Dappname=value accepts the value and sends it to the makensis script as an argument to the nsis script. The error I get is
The makensis.exe produced the following error textCommand line defined: "appname=EDMsdk O:\dev/product/NSIS/installer.nsi"
If I replace the subprocess.run with os.system as so
def run_nsis_process(self,product_name,script, logger):
NSIS_PATH='C:/Program Files (x86)/NSIS'
try:
nsis_args = '/Dappname='+product_name+ ' '+ script
nsis_cmd = 'makensis.exe /Dappname='+product_name+ ' '+ script
#process_completed = subprocess.run(['makensis.exe', nsis_args], shell=True, cwd = NSIS_PATH, universal_newlines = True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True)
os.system(nsis_cmd)
except subprocess.CalledProcessError as err:
logger.error('The makensis.exe subprocess existed with the following code {} \n The makensis.exe produced the following error text{}'.format(err.returncode, err.output))
else:
logger.info('Output from NSIS:\n{}'.format(process_completed.stdout))
than everything works and I get the installer executable.
I don't know what I'm doing wrong here, any suggestions
cheers,
es
Try this one
def run_nsis_process(self,product_name,script, logger):
NSIS_PATH='C:/Program Files (x86)/NSIS'
try:
nsis_args = ['/Dappname=', product_name, script]
process_completed = subprocess.run(['makensis.exe'] + nsis_args, shell=True, cwd = NSIS_PATH, universal_newlines = True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True)
except subprocess.CalledProcessError as err:
logger.error('The makensis.exe subprocess existed with the following code {} \n The makensis.exe produced the following error text{}'.format(err.returncode, err.output))
else:
logger.info('Output from NSIS:\n{}'.format(process_completed.stdout))
The problem is that you passed one argument
I am running cat command to read the Linux version using subprocess.run(). However it doesn't work, the error is: cat: '/etc/*-release': No such file or directory, and I can not use shell=True due to security. Any hints how to solve this is appreciated.
Here is my code:
try:
result = subprocess.run(
shlex.split("cat /etc/*-release"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
except subprocess.TimeoutExpired as err:
result = err
That's the role of the shell to evaluate the *. If you don't use it, you need to do it yourself, glob can help you for that.
So you can fix your example by doing:
from glob import glob
try:
result = subprocess.run(
["cat"] + glob("/etc/*-release"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
except subprocess.TimeoutExpired as err:
result = err
You can use the bash command so the * can be evaluated:
process = subprocess.run(['bash', '-i', '-c', 'cat /etc/*-release'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
output = subprocess.check_output(command, shell=True)
except subprocess.CalledProcessError as exc:
logger.error('There was an error while ...: \n%s',
exc.output)
raise
What is the easiest way to do the following:
Call a process using subprocess module.
If the program exited normally, put into output variable its standard output.
If the program exited abnormally, get its standard output and error.
import subprocess
process= subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process.wait() #wait for the command to finish
output= process.stdout.read()
if process.poll(): #check the error code
error= process.stderr.read()
I am calling the executable from python script using sub process call. these are the following code I have used:
try:
p = subprocess.Popen([abc.exe], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
except Exception as e:
print str(e)
from abc.exe, I have return 1 in failure case and return 0 for success case. But I don't know how to check the return value in python script.
thanks,
Popen.returncode contains the return code when the process has terminated. You can ensure that using Popen.wait.
You've saved as p the output from .communicate(), not Popen object. Perhaps try:
try:
p = subprocess.Popen(['abc.exe'], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError as e:
print str(e)
stdoutdata, stderrdata = p.communicate()
retcode = p.returncode
Another way to do this is to use subprocess.check_output() since you mention Python 2.7. This runs the command with the same arguments as Popen. The output of the command is returned as a string. If the command returns a non-zero value, a subprocess.CalledProcessError exception is raised.
So I think you can rework your code to something like this:
try:
output = subprocess.check_output(['abc.exe'], shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as ex:
# an error occurred
retcode = ex.returncode
output = ex.output
else:
# no error occurred
process(output)
Note that you can't use the stdout argument in check_output since it is used internally. Here are the docs.
So im trying to save the output from my subprocess.call but I keep getting the following error:
AttributeError: 'int' object has no attribute 'communicate'
Code is as follows:
p2 = subprocess.call(['./test.out', 'new_file.mfj', 'delete1.out'], stdout = PIPE)
output = p2.communicate[0]
You're looking for subprocess.Popen() instead of call().
You also need to change it to p2.communicate()[0].
That's because subprocess.call returns an int:
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
Run the command described by args. Wait for command to complete, then return the returncode attribute.
It looks like you want subprocess.Popen().
Here's a typical piece of code I have to do this:
p = Popen(cmd, stdout=PIPE, stderr=PIPE, bufsize=256*1024*1024)
output, errors = p.communicate()
if p.returncode:
raise Exception(errors)
else:
# Print stdout from cmd call
print output
You should use subprocess
try:
subprocess.check_output(['./test.out', 'new_file.mfj', 'delete1.out'], shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exception:
print exception.output