I am writing a small python script that needs to execute git commands from inside a given directory
The code is as follows:
import subprocess, os
pr = subprocess.Popen(['/usr/bin/git', 'status'],
cwd=os.path.dirname('/path/to/dir/'),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
(out, error) = pr.communicate()
print out
But it shows git usage as the output.
If the command doesn't involve git for eg. ['ls'] then it shows the correct output.
Is there anything I am missing ?
python version - 2.6.6
Thanks.
subprocess.Popen:
On Unix, with shell=True: […] If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself.
You don't want shell=True and also a list of arguments. Set shell=False.
Related
I use Python 3.10.7 and I am trying to get the Python interpreter to run this command:
rg mysearchterm /home/user/stuff
This command, when I run it in bash directly successfully runs ripgrep and searches the directory (recursively) /home/user/stuff for the term mysearchterm. However, I'm trying to do this programmatically with Python's subprocess.Popen() and I am running into issues:
from subprocess import Popen, PIPE
proc1 = Popen(["rg", "term", "/home/user/stuff", "--no-filename"],stdout=PIPE,shell=True)
proc2 = Popen(["wc","-l"],stdin=proc1.stdin,stdout=PIPE,shell=True)
#Note: I've also tried it like below:
proc1 = Popen(f"rg term /home/user/stuff --no-filename",stdout=PIPE,shell=True)
proc2 = Popen("wc -l",stdin=proc1.stdin,stdout=PIPE,shell=True)
result, _ = proc2.communicate()
print(result.decode())
What happens here was bizarre to me; I get an error (from rg itself) which says:
error: The following required arguments were not provided:
<PATTERN>
So, using my debugging/tracing skills, I looked at the process chain and I see that the python interpreter itself is performing:
python3 1921496 953810 0 /usr/bin/python3 ./debug_script.py
sh 1921497 1921496 0 /bin/sh -c rg term /home/user/stuff --no-filename
sh 1921498 1921496 0 /bin/sh -c wc -l
So my next thought is just trying to run that manually in bash, leading to the same error. However, in bash, when I run /bin/sh -c "rg term /home/user/stuff --no-filename" with double quotations, the command works in bash but when I try to do this programmatically in Popen() it again doesn't work even when I try to escape them with \. This time, I get errors about unexpected EOF.
As for the behavior when shell=True is specified,
the python document tells:
If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
Then your command invocation is equivalent to:
/bin/sh -c "rg" "term" "/home/tshiono/stackoverflow/221215" ...
where no arguments are fed to rg.
You need to pass the command as a string (not a list) or just drop shell=True.
I have a python script which captures repo command.
import subprocess
processing(commandforrepo)
def processing(repocmd):
process = None
process = subprocess.Popen(repocmd,
stdout=subprocess.PIPE, stderr=None, shell=True)
process.communicate()
In the particular command, I am trying to parse a list of repocmd to compare two branches and print out the differences
"repo forall $(repo forall -c 'echo $REPO_PROJECT')\
-c 'git log --abbrev-commit --pretty=oneline --no-merges \
--cherry-pick --left-only HEAD...$REPO_RREV'"
Attempted to run the script on the terminal but the command did not get executed. However, when this command is issued on the terminal, it produces a list of differences between the two branches.
Any clue as to what is missing?
Warning: most of the standard output for Git command are done on... stderr.
See here for why: informative messages are on stderr only.
So make sure to parse stderr, not stdout.
I have the following function that is used to execute system commands in Python:
def engage_command(
command = None
):
#os.system(command)
return os.popen(command).read()
I am using the os module instead of the subprocess module because I am dealing with a single environment in which I am interacting with many environment variables etc.
How can I use Bash with this type of function instead of the default sh shell?
output = subprocess.check_output(command, shell=True, executable='/bin/bash')
os.popen() is implemented in terms of subprocess module.
I am dealing with a single environment in which I am interacting with many environment variables etc.
each os.popen(cmd) call creates a new /bin/sh process, to run cmd shell command.
Perhaps, it is not obvious from the os.popen() documentation that says:
Open a pipe to or from command cmd
"open a pipe" does not communicate clearly: "start a new shell process with a redirected standard input or output" -- your could report a documentation issue.
If there is any doubt; the source confirms that each successful os.popen() call creates a new child process
the child can't modify its parent process environment (normally).
Consider:
import os
#XXX BROKEN: it won't work as you expect
print(os.popen("export VAR=value; echo ==$VAR==").read())
print(os.popen("echo ==$VAR==").read())
Output:
==value==
====
==== means that $VAR is empty in the second command because the second command runs in a different /bin/sh process from the first one.
To run several bash commands inside a single process, put them in a script or pass as a string:
output = check_output("\n".join(commands), shell=True, executable='/bin/bash')
Example:
#!/usr/bin/env python
from subprocess import check_output
output = check_output("""
export VAR=value; echo ==$VAR==
echo ==$VAR==
""", shell=True, executable='/bin/bash')
print(output.decode())
Output:
==value==
==value==
Note: $VAR is not empty here.
If you need to generate new commands dynamically (based on the output from the previous commands); it creates several issues and some of the issues could be fixed using pexpect module: code example.
I'm getting a slightly weird result from calling subprocess.Popen that I suspect has a lot to do with me being brand-new to Python.
args = [ 'cscript', '%USERPROFILE%\\tools\\jslint.js','%USERPROFILE%\\tools\\jslint.js' ]
p = Popen(args, stdout=PIPE, shell=True).communicate()[0]
Results in output like the following (the trailing double \r\n is there in case it's important)
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n
If I run that command from an interactive Python shell it looks like this
>>> args = ['cscript', '%USERPROFILE%\\tools\\jslint.js', '%USERPROFILE%\\tools\jslint.js']
>>> p = subprocess.Popen(args, stdout=subprocess.PIPE, shell=True).communicate()[0]
Lint at line 5631 character 17: Unexpected /*member 'OpenTextFile'.
f = fso.OpenTextFile(WScript.Arguments(0), 1),
...
Lint at line 5649 character 17: Unexpected /*member 'Quit'.
WScript.Quit(1);
So there's all the output I really care about, but if I dump the value of the "p" variable I just set up...
>>> p
'Microsoft (R) Windows Script Host Version 5.8\r\nCopyright (C) Microsoft Corpor
ation. All rights reserved.\r\n\r\n'
>>>
Where'd all that data I want end up going? It definitely didn't end up in "p". Looks like it's going to stdout, but I didn't I explictly tell it not to do that?
I'm running this on Windows 7 x64 with Python 2.6.6
Is it going to stderr? Try redirecting:
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True).communicate()[0]
It's probably going to stderr, as SimonJ suggested.
Also, the docs say not to use shell=True in Windows for your case:
The executable argument specifies the
program to execute. It is very seldom
needed: Usually, the program to
execute is defined by the args
argument. If shell=True, the
executable argument specifies which
shell to use. On Unix, the default
shell is /bin/sh. On Windows, the
default shell is specified by the
COMSPEC environment variable. The only
reason you would need to specify
shell=True on Windows is where the
command you wish to execute is
actually built in to the shell, eg
dir, copy. You don’t need shell=True
to run a batch file, nor to run a
console-based executable.
Later: oh wait. Are you using the shell to get those environment variables expanded? Okay, I take it back: you do need the shell=True.
I need to execute this script from my Python script.
Is it possible? The script generate some outputs with some files being written. How do I access these files? I have tried with subprocess call function but without success.
fx#fx-ubuntu:~/Documents/projects/foo$ bin/bar -c somefile.xml -d text.txt -r aString -f anotherString >output
The application "bar" also references to some libraries, it also create the file "bar.xml" besides the output. How do I get access to these files? Just by using open()?
Thank you,
Edit:
The error from Python runtime is only this line.
$ python foo.py
bin/bar: bin/bar: cannot execute binary file
For executing the external program, do this:
import subprocess
args = ("bin/bar", "-c", "somefile.xml", "-d", "text.txt", "-r", "aString", "-f", "anotherString")
#Or just:
#args = "bin/bar -c somefile.xml -d text.txt -r aString -f anotherString".split()
popen = subprocess.Popen(args, stdout=subprocess.PIPE)
popen.wait()
output = popen.stdout.read()
print output
And yes, assuming your bin/bar program wrote some other assorted files to disk, you can open them as normal with open("path/to/output/file.txt"). Note that you don't need to rely on a subshell to redirect the output to a file on disk named "output" if you don't want to. I'm showing here how to directly read the output into your python program without going to disk in between.
The simplest way is:
import os
cmd = 'bin/bar --option --otheroption'
os.system(cmd) # returns the exit status
You access the files in the usual way, by using open().
If you need to do more complicated subprocess management then the subprocess module is the way to go.
For executing a unix executable file. I did the following in my Mac OSX and it worked for me:
import os
cmd = './darknet classifier predict data/baby.jpg'
so = os.popen(cmd).read()
print so
Here print so outputs the result.