Home > Mobile >  Python run both as user and sudo
Python run both as user and sudo

Time:05-23

I'm writing a python script to automatize the installation of some tools every time I create a VM and, since I'm not a bash fan I'm doing it with python. The problem is that apt installations require sudo privileges, while pip installations don't.

I'm looking for a way to downgrade my privileges back to normal user's after all apt installations.

At the moment I tryed a pretty ugly oneliner like os.system(f"su {user} && ...") or subprocess.Popen(...)

Is there a decent way?

CodePudding user response:

You can use a list of apt commands with subprocess or pexpect. It can also prompt for a password if no password is provided using getpass:

NOTE - Tested on Ubuntu 20.04 using Python 3.8.10

NOTE - Updated to use and test pipes.quote - Thx, pts!

import pipes
import shlex
import subprocess
from getpass import getpass

import pexpect

sudo_password = '********'
# First command in double quotes to test pipes.quote - Thx, pts!
commands = ["apt show python3 | grep 'Version'",
            'sudo -S apt show python3 | grep Version', ]

print('Using subprocess...')
for c in commands:
    c = str.format('bash -c {0}'.format(pipes.quote(c)))
    # Use sudo_password if not None or empty; else, prompt for a password
    sudo_password = (
            sudo_password   '\r'
    ) if sudo_password and sudo_password.strip() else getpass()   '\r'
    p = subprocess.Popen(shlex.split(c), stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE, universal_newlines=True)
    output, error = p.communicate(sudo_password)
    rc = p.returncode
    if rc != 0:
        raise RuntimeError('Unable to execute command {0}: {1}'.format(
            c, error.strip()))
    else:
        print(output.strip())

print('\nUsing pexpect...')
for c in commands:
    c = str.format('bash -c {0}'.format(pipes.quote(c)))
    command_output, exitstatus = pexpect.run(
        c,
        # Use sudo_password if not None or empty; else, prompt for a password
        events={
            '(?i)password': (
                    sudo_password   '\r'
            ) if sudo_password and sudo_password.strip() else (
                    getpass()   '\r')
        },
        withexitstatus=True)
    if exitstatus != 0:
        raise RuntimeError('Unable to execute command {0}: {1}'.format(
            c, command_output))
    # For Python 2.x, use string_escape.
    # For Python 3.x, use unicode_escape.
    # Do not use utf-8; Some characters, such as backticks, may cause exceptions
    print(command_output.decode('unicode_escape').strip())

Output:

Using subprocess...
Version: 3.8.2-0ubuntu2
Version: 3.8.2-0ubuntu2

Using pexpect (and getpass)...

Version: 3.8.2-0ubuntu2
[sudo] password for stack: 

Version: 3.8.2-0ubuntu2

Process finished with exit code 0
  • Related