Run shell builtin command in Python - python

For training, I have idea to write a script which will display the last bash/zsh command.
First of all, I tried with os.system and subprocess to execute history command. But, as you know, history is a shell builtin, so, it doesn't return anything.
Then, I tried this piece of code:
shell_command = 'bash -i -c "history -r; history"'
event = Popen(shell_command, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
But it have just shown commands from last session. What i want to see is the previous command (which i just typed)
I tried cat ~/.bash_history and have same result, unluckily.
Any idea ?

You could use tail to get the last line:
from subprocess import Popen, PIPE, STDOUT
shell_command = 'bash -i -c "history -r; history"'
event = Popen(shell_command, shell=True, stdin=PIPE, stdout=PIPE,
stderr=STDOUT)
out = Popen(["tail", "-n", "1"], stdin=event.stdout, stdout=PIPE)
output = out.communicate()
print(output[0])
Or just split the output and get the last line:
from subprocess import Popen, PIPE, STDOUT
shell_command = 'bash -i -c "history -r; history"'
event = Popen(shell_command, shell=True, stdin=PIPE, stdout=PIPE,
stderr=STDOUT)
print(event.communicate()[0].splitlines()[-1])
Or read bash_history:
from os import path
out= check_output(["tail","-n","1",path.expanduser("~/.bash_history")])
print(out)
Or open the file in python and just iterate until you get to the end of the file:
from os import path
with open(path.expanduser("~/.bash_history")) as f:
for line in f:
pass
last = line
print(last)

Related

How can I use run instead of communicate when providing text on stdin?

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.

Antiword with UTF-8 in python

This is my code :
from subprocess import Popen, PIPE
cmd = ['antiword', 'tbhocbong151.doc']
p = Popen(cmd, stdout=PIPE)
stdout, stderr = p.communicate()
print(stdout.decode('utf-8', 'ignore'))
I have content in file word like this : "Chào bạn"
but when I generated output is: "Ch?o b?n"
How can I fix it to output like input ?
Thanks for your help
I believe that the problem is that the locale is not properly set when antiword is running. Try this:
import os
from subprocess import Popen, PIPE
myenv = dict(os.environ)
if 'LC_ALL' in myenv:
del myenv['LC_ALL']
myenv['LANG'] = 'en_US.UTF-8'
cmd = ['antiword', 'tbhocbong151.doc']
p = Popen(cmd, stdout=PIPE, env=myenv)
stdout, stderr = p.communicate()
print(stdout.decode('utf-8', 'ignore'))
If that doesn't work, try setting the LANG env variable in your shell before running your python program; e.g. by doing:
export LANG=en_US.UTF-8

python how to use subprocess pipe with linux shell

I have a python script search for logs, it continuously output the logs found and I want to use linux pipe to filter the desired output. example like that:
$python logsearch.py | grep timeout
The problem is the sort and wc are blocked until the logsearch.py finishes, while the logsearch.py will continuous output the result.
sample logsearch.py:
p = subprocess.Popen("ping google.com", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for line in p.stdout:
print(line)
UPDATE:
figured out, just change the stdout in subprocess to sys.stdout, python will handle the pipe for you.
p = subprocess.Popen("ping -c 5 google.com", shell=True, stdout=**sys.stdout**)
Thanks for all of you help!
And why use grep? Why don't do all the stuff in Python?
from subprocess import Popen, PIPE
p = Popen(['ping', 'google.com'], shell=False, stdin=PIPE, stdout=PIPE)
for line in p.stdout:
if 'timeout' in line.split():
# Process the error
print("Timeout error!!")
else:
print(line)
UPDATE:
I change the Popen line as recommended #triplee. Pros and cons in Actual meaning of 'shell=True' in subprocess

How to use subprocess.Popen with built-in command on Windows

In my old python script, I use the following code to show the result for Windows cmd command:
print(os.popen("dir c:\\").read())
As the python 2.7 document said os.popen is obsolete and subprocess is recommended. I follow the documentation as:
result = subprocess.Popen("dir c:\\").stdout
And I got error message:
WindowsError: [Error 2] The system cannot find the file specified
Can you tell me the correct way to use the subprocess module?
You should use call subprocess.Popen with shell=True as below:
import subprocess
result = subprocess.Popen("dir c:", shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output,error = result.communicate()
print (output)
More info on subprocess module.
This works in Python 3.7:
from subprocess import Popen, PIPE
args = ["echo", "realtime abc"]
p = Popen(args, stdout=PIPE, stderr=PIPE, shell=True, text=True)
for line in p.stdout:
print("O=:", line)
.
Output:
O=: "realtime abc"

Subprocess.Popen spits output on screen even with stdout=subprocess.PIPE)

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)

Categories

Resources