Issues with subprocess.Popen() replacing os.system() - python

I am trying to generate a set of files through a python script on a 48-core cluster which actually has a master-and 3 slave machines. I need to generate a set of files, run some scripts on them, collect results and then delete them. I again repeat the process- regenerate files, execute, delete etc.,
When I delete and regenerate files with the same name, I see that the slave machine complains that it cannot find the files.
I am running python script through os.system()
I learnt from this post that it is better to use subprocess.Popen() rather than os.system so that it actually waits for my script to generate my files, before proceeding with the execution. I could use os.system("pause") or time.sleep(whatever) for waiting, but I want to convert my os.systems to subprocess.popens or subprocess.calls and I am stuck here.
I ran through the python documentation and tried out subprocess.Popen('ls'), but I am not able to get a simple thing like subprocess.Popen('cd /whatever_directory') working.
It might sound silly but how do I execute such a simple command like cd through subprocess rather than os.system('cd')?
Then, I actually want to convert the following into subprocess. How do I do it?
import os,optparse
from optparse import OptionParser
parser.add_option("-m", "--mod",dest='module', help='Enter the entity name')
parser.add_option("-f", "--folder", dest="path",help="Enter the path")
module=options.module
path=options.path
os.system('python %s/python_repeat_deckgen_remote.py -m %s' %(path,module))
I just replaced os.system with subprocess.Popen.
But it gave me a lot of complaints:
File "/usr/lib64/python2.6/subprocess.py", line 633, in __init__
errread, errwrite)
File "/usr/lib64/python2.6/subprocess.py", line 1139, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory

As NPE already noted, new processes don't affect the existing one (which means os.system('cd /some/where') also has no effect on the current process). In this case, though, I think you're tripping over the fact that os.system invokes a shell to interpret the command you pass in, while subprocess.Popen does not do so by default. But you can tell it to do so:
proc = subprocess.Popen(
'python %s/python_repeat_deckgen_remote.py -m %s' % (path, module),
shell = True)
status = proc.wait()
If you're invoking a shell built-in command or using shell expansions, it's necessary to invoke the shell (assuming you're not willing to simulate it):
>>> import subprocess
>>> x = subprocess.Popen('echo $$')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/subprocess.py", line 679, in __init__
errread, errwrite)
File "/usr/local/lib/python2.7/subprocess.py", line 1249, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
>>> x = subprocess.Popen('echo $$', shell = True); x.wait()
81628
0
>>>
but you can bypass the shell—which can help with security issues—if your problem permits, by breaking up the command arguments into a list:
>>> x = subprocess.Popen(['echo', '$$']); x.wait()
$$
0
>>>
Note that this time the output here is the string $$, rather than a process ID, since this time the shell did not interpret the string.
For your original example, for instance, you might use:
proc = subprocess.Popen(['python',
os.path.join(path, 'python_repeat_deckgen_remote.py'),
'-m',
module])
which avoids issues if path and/or module contain characters special to the shell.
(You might even want to call subprocess.Popen with cwd = path and eliminate the call to os.path.join, although that depends on other things.)

I am not able to get a simple thing like subprocess.Popen('cd /whatever_directory') working.
Each process's current working directory is independent of other processes in the system.
When you start a subprocess (using either of these mechanisms), and get it to cd into another directory, that has no effect on the parent process, i.e. on your Python script.
To change the current working directory of your script, you should use os.chdir().

I might be not answering you question directly, but for such tasks which require running subprocess, I always use plumbum.
IMO, It makes the task much simpler and more intuitive, including running on remote machines.
Using plumbum, in order to set subprocess's working directory, you can run the command in a with local.cwd(path): my_cmd() context.

Related

Python check_output call to eval (with arguments) fails

I'm using check_output to do all my SSH and GitHub setup, and I'm attempting to execute eval $(ssh-agent), both to start the agent and to parse the output if I need the process id.
from subprocess import check_output
out = check_output(["eval", "$(ssh-agent)"])
print(out)
But regardless of how I escape things, I get the same error.
Traceback (most recent call last):
File "gitSetup.py", line 3, in <module>
out = check_output(["eval", "$(ssh-agent)"])
File "/usr/lib/python2.7/subprocess.py", line 216, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
I'm wondering if I'm not escaping or trimming my arguments properly; if anyone sees my error, I'd appreciate some insight!
Even if you managed to fix the superficial syntax problems, a subprocess which runs successfully simply then terminates without a trace; it doesn't attempt to (and couldn't, even if it wanted to) modify the environment of the parent Python process. This is a common FAQ.
You could run the eval in the parent process which starts your Python script, or perhaps figure out how to communicate with ssh-agent directly from Python. Its output is usually a simple sequence of variable assignments, which you can parse yourself.
from subprocess import check_output
from os import environ
eval_string = check_output(['ssh-agent'])
for line in eval_string.rstrip('\n').split('\n'):
for expr in line.rstrip(';').split(';'):
if expr.startswith((' export ', 'echo ')):
continue
var, value = expr.strip().split('=', 1)
environ[var] = value
If the output from ssh-agent contains shell quoting, you will need to perform additional parsing on it (basically, trim the quotes around the value string). But this is already rather clunky and brittle, so perhaps revert to setting up the environment before launching Python instead of trying to splash some sophistication onto this kludge.
In more detail, ssh-agent and a precious few other shell utilities have a very specific design in order for them to be able to communicate with their parent process. Precisely because a subprocess cannot make any changes in the environment of its parent process, it instead prints a piece of code for its parent process to execute. By default, it prints sh code like this:
SSH_AUTH_SOCK=/tmp/ssh-MUyniqn10506/agent.10506; export SSH_AUTH_SOCK;
SSH_AGENT_PID=10507; export SSH_AGENT_PID;
echo Agent pid 10507;
There is also an option for it to print similar code in csh syntax (this is a historical shell which thankfully isn't used much any longer) but, alas, no option for producing Python code. (It would not be hard to make ssh-agent do that, per se.)
(The above output sample copy/pasted from http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/ which contains a fuller explanation.)

Python : Execute several commands in CMD in single instance

I am trying to call several "blocking" .bat files from my python program. First thing I need to do is change the directory that the CMD is opened. After I have the CMD pointing to the desired location I will call two bat files. I want them to execute sequentially.
def launchAdminConsole():
print('Going to launch admin console')
changeDir = 'cd dir1\\dir2\\bin \n 1.bat \n 2.bat'
os.system("start /wait cmd /c {"+changeDir+"}")
print("Admin Console launced")
According to this question using the /wait should make the command prompt wait but for me, it just pops up and goes away so I am not sure if the bat file is executed or not.
Also I am not sure if I have formed the command line code correctly. I googled about how to execute several commands in a single instance of cmd from python but none of the results helped me much so I took a guess on my own and did the above code.
I need the command prompt to open and run the two bat files and then give the control back to python. I do not need to fetch the output of the bat files or something I just need to know if the two files are executed or not. As I said before they are all blocking bat files so if run correctly the command prompt will not be able to close so quickly. I hope you guys got my requirement else comment below I will explain more.
Edit :
Updated my code as follows
def launchAdminConsole2():
print('Going to launch admin console')
changeDir = 'cd dir1\\dir2\\bin'
runOnce1 = '1.bat'
runOnce2 = '2.bat'
p = subprocess.Popen(changeDir,shell=True)
p.wait()
print(p.returncode)
p = subprocess.call([changeDir, runOnce, runOnce1])
p.wait()
print("Admin Console launced")
The return code for the change directory returns 0 but it is saying that 1.bat is not found. I am sure that if the directory has changed that file will be present in the given location.
The error is
File "C:\Users\nirma\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 304, in call
with Popen(*popenargs, **kwargs) as p:
File "C:\Users\nirma\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 756, in __init__
restore_signals, start_new_session)
File "C:\Users\nirma\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 1155, in _execute_child
startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified
You can run several commands using cmd.exe /c if you separate them with ampersands:
cmd.exe /c "cd \dir & 1.bat & 2.bat"
Try passing that to subprocess.call and see how it goes.

subprocess.py - No such file or directory [duplicate]

This question already has answers here:
python subprocess.call() "no such file or directory"
(2 answers)
Closed 4 years ago.
I'm attempting to run an old script which takes an .mdb file and converts it into a MySQL database. However, I'm running into an issue where I receive the following error.
File "/usr/lib64/python2.7/subprocess.py", line 568, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib64/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/usr/lib64/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
I have attempted to research this error, but all I found was fixes where the item was not correctly formatted as a list. So, I've modified the affected code where subprocess.py is called, and changed it to the following:
def get_external_command_output(command):
args = shlex.split(command)
print "################"
print type(args), args
ret = subprocess.check_output(args) # this needs Python 2.7 or higher
return ret
Which in turn returns the same error, but also the below message is printed:
<type 'list'> ['mdb-tables', '-1', '/mnt/data/General/Genit/WIP/IL_Orders.mdb']
So I can safely say that the arguments are correctly formatted as a list. I'm really unsure how else to tackle this issue, because all the help forums I've found suggest the same issue. The argument isn't a list. However, I can see that mine is.
Could anybody provide some guidance here?
Many thanks.
There can be number of reasons for this. The error comes from your underlying OS (ENOENT).
The easiest to would be to try running the same thing:
mdb-tables -1 /mnt/data/General/Genit/WIP/IL_Orders.mdb
(or just mdb-tables really) and see what happens, what complain you get from your shell.
Among possible reasons would be:
As mentioned by Peter Wood in the comments. If you just pass an executable name to run, it must be located in one of the directories listed in your PATH environmental variable. Or, you have to use either path to the executable which is either absolute or relative to current working directory (based on where you ran the parent from or cwd argument passed to subprocess.check_output call). E.g. ./mdb-tables if in the same directory where you were when you ran the parent script or to which cwd was set.
It is also possible. Esp. if it's an older script. That you have specified an interpreter that no longer exists on your host. In that case the same error (ENOENT) would be raised. You might see a slightly different error message (hinting at bad interpreter) if executed directly from shell, but it would look identical when called as subprocess from python.
You've hinted mdb-tables is also a script. But otherwise previous paragraph would also hold true for dynamically linked ELF binaries. If they were built with and expected a version (path) of dynamic linker (their interpreter) no longer available on the system. You can print path of the expected dynamic linker binary for instance by running objdump -sj .interp BINARY_IN_QUESTION.

running emconfigure exception with Popen

When I run emconfigure ./configure I get the following error:
ERROR:root:Exception thrown when invoking Popen in configure with
args: "./configure"!
Traceback (most recent call last):
File "/usr/local/bin/emconfigure", line 13, in <module>
emconfigure.run()
File "/usr/local/Cellar/emscripten/1.37.21/libexec/emconfigure.py", line 46, in run
shared.Building.configure(sys.argv[1:])
File "/usr/local/Cellar/emscripten/1.37.21/libexec/tools/shared.py", line 1533, in configure
process = Popen(args, stdout=None if EM_BUILD_VERBOSE_LEVEL >= 2 else stdout, stderr=None if EM_BUILD_VERBOSE_LEVEL >= 1 else stderr, env=env)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
Break-down:
This one can be tricky to track down because of the depth of the call stack. Let's break it apart:
At the "next-to-top level": a file you're trying to execute cannot be found.
One level lower than that things become clear: subprocess.Popen is trying to execute args.
What's in args? - basically ./configure (as passed by emscripten). export the EM_BUILD_VERBOSE=3 environmental variable and then stderr should display this.
So, the next-to-top level ./configure is directing things in an invalid fashion. Now it's time for a guess at the top level (not knowing what's in the ./configure script): is some line of the shell command in the ./configure script file itself trying to execute something non-existent? E.g.: some point after #! /bin/sh ? - try debugging just ./compile.
It is worth noting that a subprocess can have issues with relative paths (not getting PATH), and permissions (./configure is locked-down), but your error very well may persist even after /you/make/an/absolute/call/to/configure and 777 the permissions of configure. - In that case the previous paragraph is the likeliest culprit.
Editorializing:
The reason this error is interesting as a general one for emscripten is that part (much) of emscripten's raison d'etre is to port [legacy] C/++ code to new JS apps. Well, old C/++ builds get old in the sense of this question. If that's true, this is will be a generally-occurring problem, and, if it is general the community could use a general pattern for resolution.
If you're using Docker, your host computer is Windows, and you mounted it to a Windows directory, make sure that your files aren't \r\n line endings but instead \n line endings. Changing this fixed it for me.
In this case, if you're using git, you might try setting git config core.autocrlf input to stop checking out to \r\n. If you need to convert a file, try this answer out.

Subprocess module fails to run command

I'm trying to execute Google's cpplint.py on a group of my files and collect the results to one log file. However, I have not managed to beat the subprocess module. My current code is here:
import os, subprocess
rootdir = "C:/users/me/Documents/dev/"
srcdir = "project/src/"
with open(rootdir+srcdir+"log.txt", mode='w', encoding='utf-8') as logfile:
for subdir, dirs, files in os.walk(rootdir+srcdir):
for file in files:
if file.endswith(".h") or file.endswith(".cpp"):
filewithpath=os.path.join(subdir, file)
cmd=['c:/Python27/python.exe','C:/users/me/Documents/dev/cpplint.py','--filter=-whitespace,-legal,-build/include,-build/header_guard/', filewithpath]
output = subprocess.check_output(cmd)
logfile.write(output.decode('ascii'))
Trying to run the above code throws an error:
File "C:\Python32\lib\site.py", line 159
file=sys.stderr)
^ SyntaxError: invalid syntax Traceback (most recent call last): File "C:\Users\me\Documents\dev\project\src\verifier.py", line 19, in <module>
output = subprocess.check_output(cmd) File "C:\Python32\lib\subprocess.py", line 511, in check_output
raise CalledProcessError(retcode, cmd, output=output) subprocess.CalledProcessError: Command '['c:/Python27/python.exe', 'C:/users/me/Documents/dev/cpplint.py', '--filter=-whitespace,-legal,-build/include,-build/header_guard/', 'C:/users/me/Documents/dev/project/src/aboutdialog.cpp']' returned non-zero exit status 1
If I substitute the cmd with something simpler like:
cmd=['C:/WinAVR-20100110/bin/avr-gcc.exe','--version']
Then the script works as expected.
I have also tried to use a single command string instead of a list of strings as cmd, but the result is the same.
When debugging the code, I copied the list-of-strings-turned-into-the-command-line-command from the debugger and ran it in the Windows command line, and the command ran as expected.
The Python interpreter running my script is Python 3.2.
Any tips are greatly appreciated.
Looks like cpplint.py is simply exiting with a non-zero return code - which it might do, for instance, if it finds errors or "lint" in the source files it is checking.
See the documentation for subprocess.check_output. Note that if the command executed returns a non-zero exit code then a subprocess.CalledProcessError is raised.
You could work around it by watching for CalledProcessError, e.g.
try:
output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as e:
# ack! cpplint.py failed... report an error to the user?
EDIT:
The SyntaxError seems to be the key here, and is probably caused by C:\Python32\lib being in your PYTHONPATH (either explicitly, or, this could happen if it is your current working directory).
The Python interpreter (since about 1.5.2-ish) automatically runs import site when started. So, when this is the case, and your script goes to execute:
c:/Python27/python.exe C:/users/me/Documents/dev/cpplint.py ...
then the Python 2.7 interpreter will find C:\Python32\lib\site.py first, and try to load that, instead of the one (presumably) at C:\Python27\lib\site.py. The issue is that Python 3's site.py contains syntax incompatible with Python 2, so the process launched by subprocess.check_output is failing with a SyntaxError before it even gets a chance to run cpplint, which propagates the CalledProcessError.
Solution? Make sure Python2 gets a bonafide Python2 "PYTHONPATH", and likewise for Python3! In other words, make sure C:\Python32\lib is not in the PYTHONPATH search path when running the Python2 interpreter.
One way to do this in your case is to set an explicit environment when launching the process, e.g.:
python2_env = {"PYTHONPATH": "path/to/python2/stuff:..."}
output = subprocess.check_output(cmd, env=python2_env)
I would request you to run this first
pipe = subprocess.Popen([cmd, options],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = pipe.communicate()
You will get to know what exactly is the error behind it, since CalledProcessError is raised only if the exit code was non-zero.
I did it by replacing the def main() with the following (I editet the errorfunction too to get a proper csv-file):
errorlog = sys.stderr
sys.stderr = open("errorlog.csv","w")
sys.stderr.write("File;Line;Message;Category;Confidence\n")
for filename in filenames:
ProcessFile(filename, _cpplint_state.verbose_level)
_cpplint_state.PrintErrorCounts()
sys.exit(_cpplint_state.error_count > 0)
sys.stdout = errorlog
sys.stderr.close()

Categories