I have an external application called gmx which works like so:
gmx trjconv -f test.trr -s test.tpr -o test.xtc -center -fit progressive -ur compact << eof
1
1
0
eof
This commands creates the file test.xtc in the current directory.
I would like to call this from a python subprocess. I tried the following:
p = subprocess.Popen(
['gmx', 'trjconv', '-f test.trr', '-s test.tpr', '-o test.xtc', '-center', '-ur compact', '-fit progressive'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(b'1\n1\n0\n')
p.stdin.close()
But it fails silently in the sense that no error occurs but nothing happens. I read some posts about passing eof but I could not adapt them to my needs. Would you have any idea about how to do this ?
The problem was in the way my Popen was written. Rewriting it as:
p = subprocess.Popen(
['gmx', 'trjconv', '-f', 'test.trr', '-s', 'test.tpr', '-o', 'test.xtc', '-center', '-ur', 'compact', '-fit', 'progressive'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.communicate(b'1\n1\n0')
did the job.
There were at least two mistakes:
the arguments list must contain the arguments one by one (e.g. not
'-f test.trr' but '-f', 'test.trr')
using communicate when using both stdin and stdout
Related
trying to figure out how to do this:
command = f"adb -s {i} shell"
proc = Popen(command, stdin=PIPE, stdout=PIPE)
out, err = proc.communicate(f'dumpsys package {app_name} | grep version'.encode('utf-8'))
but in this:
command = f"adb -s {i} shell"
proc = run(command, stdin=PIPE, stdout=PIPE, shell=True)
out, err = run(f'dumpsys package {app_name} | grep version', shell=True, text=True, stdin=proc.stdout )
The idea is to make a command which require input of some kind( for example(entering shell)) and afterwards inserting another command to shell.
I've found a way online with communicate, But I wonder how to do it with run() func.
Thanks!
You only need to call run once -- pass the remote command in the input argument (and don't use shell=True in places where you don't need it).
import subprocess, shlex
proc = subprocess.run(['adb', '-s', i, 'shell'],
capture_output=True,
input=f'dumpsys package {shlex.quote(app_name)} | grep version')
shlex.quote prevents an app name that contains $(...), ;, etc from running unwanted commands on your device.
When I execute below program, it list file correctly.
import subprocess
foo = subprocess.run("ls /home/my_home",
shell=True,
executable="/bin/bash",
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
my_std_out = foo.stdout.decode("utf-8")
But when execute below program, there is nothing in stdout.
import subprocess
foo = subprocess.Popen(["ls /home/my_home"],
shell=True,
executable="/bin/bash",
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
my_std_out = foo.stdout.read().decode("utf-8")
I wonder is there anything wrong with my second part program?
Thankyou in advance!
From python docs:
"communicate() returns a tuple (stdout_data, stderr_data). The data will be strings if streams were opened in text mode; otherwise, bytes."
Therefore, if you'd like to get output via Popen, you have to unpack the retruned tuple from communicate() like this:
out, err = foo.communicate()
In [150]: out
Out[150]: b''
In [151]: err
Out[151]: b"ls: cannot access '/home/my_home': No such file or directory\n"
I think the bash command and the path should be placed between quotes each when you use brackets like the following
import subprocess foo = subprocess.Popen(["ls", "/home/my_home"], shell=True, executable=/bin/bash, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) my_std_out = foo.stdout.read().decode("utf-8")
I want to input one argument to the subprocess.communicate, but it always be out of my expects.
the folder tree:
├── file1
├── file2
├── main.py
the content of main.py:
import subprocess
child = subprocess.Popen(["ls"], stdin=subprocess.PIPE, universal_newlines=True)
filepath = '/Users/haofly'
child.communicate(filepath)
whatever i change the filepath to, the result only lists current folder(file1,file2,main.py).
Is I misunderstanding the communicate? How I send data to the Popen?
And how about ssh command if i want to send password?
subprocess.Popen(['ssh', 'root#ip'], stdin=subprocess.PIPE, universal_newlines=True)
You cannot 'pipe' data into ls - it lists directories based on the provided CLI arguments - but you should be able to use xargs to achieve what you want (essentially passing your folder as an argument to ls) if you don't want to provide it with the command itself:
import subprocess
child = subprocess.Popen(["xargs", "ls"], stdin=subprocess.PIPE, universal_newlines=True)
filepath = '/Users/haofly'
child.communicate(filepath)
When you use ls manually, do you type ls alone, hit Enter, and then type in the filepath in response to a prompt? That's how you're trying to use it here - the parameter to .communicate() becomes the subprocess's standard input, which in fact ls ignores completely. It wants the directory to list as a command-line parameter, which you would specify as ["ls", filepath].
I think you are missing the a shell parameter in your Popen call:
import subprocess
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
process.wait()
print process.returncode
'ls' is probably not the best command to illustrate the concept, but if you want to pass in arguments to a command you would have to do something similar to this:
cmd = ['cmd', 'opt1', 'optN']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
out, err = p.communicate('args')
print out
I'm using multiple commands to run:
e.g. cd foo/bar; ../../run_this -arg1 -arg2="yeah_ more arg1 arg2" arg3=/my/path finalarg
Running with:
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
(out, err) = p.communicate()
But this spits output on screen (Python 2.7.5)
And out is empty string.
You have shell=True, so you're basically reading the standard output of the shell spawned, not the standard output of the program you want to run.
I'm guessing you're using shell=True to accommodate the directory changing. Fortunately, subprocess can take care of that for you (by passing a directory via the cwd keyword argument):
import subprocess
import shlex
directory = 'foo/bar'
cmd = '../../run_this -arg1 -arg2="yeah_ more arg1 arg2" arg3=/my/path finalarg'
p = subprocess.Popen(shlex.split(cmd), cwd=directory, stdout=subprocess.PIPE)
(out, err) = p.communicate()
As per comment I added stderr too and that worked!:
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
In windows I have to execute a command like below:
process = subprocess.Popen([r'C:\Program Files (x86)\xxx\xxx.exe', '-n', '#iseasn2a7.sd.xxxx.com:3944#dc', '-d', r'D:\test\file.txt'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
process.communicate()
This works fine in python interactive mode, but not at all executing from the python script.
What may be the issue ?
Popen.communicate itself does not print anything, but it returns the stdout, stderr output. Beside that because the code specified stdout=PIPE, stderr=... when it create Popen, it catch the outputs (does not let the sub-process print output directly to the stdout of the parent process)
You need to print the return value manually:
process = ....
output, error = process.communicate()
print output
If you don't want that, don't catch stdout output by omit stdout=PIPE, stderr=....
Then, you don't need to use communicate, but just wait:
process = subprocess.Popen([...], shell=True)
process.wait()
Or, you can use subprocess.call which both execute sub-process and wait its termination:
subprocess.call([...], shell=True)