Unable to get send sudo password to subprocess.Popen() successfully in Python for bash - python-3.x

I'm attempting to create a python script to compile github surface kernel using their recommended steps.
https://github.com/dmhacker/arch-linux-surface
So far I'm stuck at a couple of sections.
Per the instructions for compiling the setup.sh must be run using sudo.
I've tried sending in the password before calling process using
preproc = subprocess.Popen(password, shell=True, stdout=subprocess.PIPE)
process = subprocess.Popen(["sudo", 'sh setup.sh'], shell=True, stdin=preproc.stdout, encoding='utf8')
I've tried sudo -S which doesn't seem to work at all. I've also tried lowercase -s.
I've tried changing subprocess.Popen to subprocess.call
password = getpass.getpass()
process = subprocess.Popen(["sudo", 'sh setup.sh'], shell=True,
stdin=subprocess.PIPE, encoding='utf8')
print(process.communicate(password + "\n"))
process.wait()
I expected the shell to be run at sudo level but it's not.

I'm not exactly sure what the difference is as I've since gone through many iterations, but finally got it to work and simplified. Hope this helps someone in the future.
import getpass
from subprocess import Popen, PIPE
password = getpass.getpass()
command = "./setup.sh"
process = Popen(['sudo', '-S', command], stdout=PIPE, encoding='utf8')
process.communicate(password)

Related

Running a string command using exec with popen

I have a simple cmd_str containing a set of lines. Using exec, I can run those lines juts fine. However, running those lines in a separate process when shell=True is failing. Is this dues to missing quotes? what is happening under the hood?
import subprocess
cmd_str = """
import sys
for r in range(10):
print('rob')
"""
exec(cmd_str) # works ok
full_cmd = f'python3 -c "exec( "{cmd_str}" )"'
process = subprocess.Popen([full_cmd],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(output, error) = process.communicate()
exit_code = process.wait()
output_msg = output.decode("utf-8", 'ignore')
error_msg = error.decode("utf-8", 'ignore').strip()
Your approach is slightly inaccurate. I believe the problem you're having has to do with the subprocess usage. The first thing you must realise is that exec
is a way to send and execute python code to and from the interpreter directly. This is why it works inside python programs(and it is generally not a good approach). Subprocesses on the other hand handle command like they are being called directly from the terminal or shell. This means that you no longer need to include exec cause you are already interacting with the python interpreter when you call python -c.
To get this to run in a subprocess environment all you need to do is
full_cmd = f'python3 -c "{cmd_str}"'
process = subprocess.Popen(full_cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
Also, notice the absence of square brackets when calling subprocess.Popen, this is because that particular approach works slightly different and if you want to use the square brackets your command will have to be
full_cmd = ['python3', '-c', f'{cmd_str}']
process = subprocess.Popen(full_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
And with these few changes, everything should work OK

How to get command's standard output with subprocess?

For some reason, I want to gather the help message of some commands. In order to do it, I use the subprocess module in Python3. My code works fine for linux commands but not when I use it on BASH commands. Typically I want it to work on the cd BASH command.
Here is the snippet of code I use for now:
import subprocess
instruction = ["cat", "--help"]
proc = subprocess.run(instruction, stdout=subprocess.PIPE,stderr=subprocess.PIPE, universal_newlines=True)
return proc.stdout
As said before, it works fine and it returns the help message of the command cat.
Here is what it returns when I try to adapt my code in order to handle BASH commands:
>>> import subprocess
>>> subprocess.run("cd --help", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout
b''
My question is simple, is it possible to get BASH commands' help message using Python3. If so, how to do that ?
You can take a look at what subprocess.run returns:
>>> import subprocess
>>> result = subprocess.run("cd --help", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
>>> result
CompletedProcess(args='cd --help', returncode=1, stdout=b'', stderr=b'/bin/sh: line 0: cd: --: invalid option\ncd: usage: cd [-L|-P] [dir]\n')
Turns out, cd --help is an error:
$ cd --help
-bash: cd: --: invalid option
cd: usage: cd [-L|-P] [dir]
So you should look for it in result.stderr.

Popen returning nothing

Im trying to run a exe with some arguments within python using subprocesses Popen but the code is currently printing nothing as the output and a CMD window doesnt open when i run the file.
I know the command works as i manually paste it within CMD and it runs perfectly fine, what could i be doing wrong in my code?
import sys
import admin
import subprocess
from subprocess import Popen, PIPE
Path_2to3 = sys.executable[:-11] + "Scripts"
cmdCommand = '''cd "{}" && 2to3.exe -w "C:/Users/Desktop/bulk_converter/tests/test2.py"'''.format(Path_2to3)
process = subprocess.Popen(cmdCommand, stdout=subprocess.PIPE, shell=True)
output, error = process.communicate()
print(output, error)
retured values from print(output,error)
b'' None
Any help/suggestions would be appreciated.
Hi perhaps also specify stderr should be redirected to a pipe:
stderr=subprocess.PIPE
You may also be interested in passing a timeout parameter to communicate.

Python3. Prompt requires ctrl+c and halts script

I'm writing a small script that runs through a directory and attempts to obtain version numbers by running "filename --version". Now for the most part it works or fails in a manageable way. However I've come across one file "iptables-restore" in /sbin/ that fails when --version is passed to it. However the failure leaves the prompt in a state that requires a ctrl+z or ctrl+c to return to the prompt, and thus halts my script.
Here's the code:
try:
subOut = subprocess.check_output([fname, "--version"])
except subprocess.CalledProcessError as cpE:
fObj.write('{0:25}RETURN CODE:{1:15}\t\t{2:30}\n'.format(fnamecolon, cpE.returncode, cpE.output))
fnamecolon = ''
pass
except Exception as e:
pass
I just wondered if there's an elegant way to handle this - either via a return code or perhaps an Exception.
I'm fairly new to Python and am doing this for practice really - so any guidance is greatly appreciated.
Thanks all!
So this works better - probably still some things I'm not understanding though...
try:
# devnull=open(os.devnull, 'w')
try:
from subprocess import DEVNULL # Python 3
except ImportError:
DEVNULL = open(os.devnull, 'r+b', 0)
subOut = Popen([fname, "--version"], stdin=DEVNULL, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = subOut.stdout.read()
except subprocess.CalledProcessError as cpE:
fObj.write('{0:25}RETURN CODE:{1:15}\t\t{2:30}\n'.format(fnamecolon, cpE.returncode, cpE.output))
fname = ''
pass
except Exception as e:
# fObj.write('{0:30}{1:30}{2:30}\n'.format(fname, e.returncode, e.output))
pass
I can reproduce it: iptables-restore --version commands waits for input without stdin=subprocess.DEVNULL suggested by #mata:
#!/usr/bin/env python3
import subprocess
cp = subprocess.run(['iptables-restore', '--version'],
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print(cp.returncode, cp.stdout, cp.stderr)

How can I have subprocess or Popen show the command line that it has just run?

Currently, I have a command that looks something like the following:
my_command = Popen([activate_this_python_virtualenv_file, \
"-m", "my_command", "-l", \
directory_where_ini_file_for_my_command_is + "/" + my_ini_file_name], \
stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=False,
universal_newlines=False, cwd=directory_where_my_module_is)
I have figured out how to access and process the output, deal with subprocess.PIPE, and make subprocess do a few other neat tricks.
However, it seems odd to me that the standard Python documentation for subprocess doesn't mention a way to just get the actual command line as subprocess.Popen puts it together from arguments to the Popen constructor.
For example, perhaps my_command.get_args() or something like that?
Is it just that getting the command line run in Popen should be easy enough?
I can just put the arguments together on my own, without accessing the command subprocess runs with Popen, but if there's a better way, I'd like to know it.
It was added in Python 3.3. According to docs:
The following attributes are also available:
Popen.args The args argument as it was passed to Popen – a sequence of
program arguments or else a single string.
New in version 3.3.
So sample code would be:
my_args_list = [] # yourlist
p = subprocess.Popen(my_args_list)
assert p.args == my_args_list

Resources