Call a shell command from Python (was: Calling Bash Command From Python)

Ben Finney ben+python at benfinney.id.au
Mon Oct 31 00:44:13 EDT 2016


Wildman via Python-list <python-list at python.org> writes:

> Python 2.7.9 on Linux
>
> Here is a bash command that I want to run from a python
> program:  sudo grep "^user\:" /etc/shadow

Some points to note:

* Those commands are not special to Bash, or any particular shell. They
  invoke commands, without AFAIK any specific Bash features. So this is
  asking rather to invoke a shell command.

  Nothing wrong with that; but on that basis, I've changed the subject
  field.

* You're asking to invoke the ‘sudo’ command, which itself is designed
  to switch to a separate user identity and run another program.

* The above command is (I assume) typed into a shell, but your Python
  program never invokes Bash or any other shell.

> If I enter the command directly into a terminal it works perfectly.

Note that ‘sudo’ is specifically designed to be invoked interactively,
seeking to verify that the current user has credentials to run the
command.

Note further that ‘sudo’ will record when the *current user session*
last invoked ‘sudo’ and seek re-verification if that is too long in the
past.

Both of these are security measures, and are designed to avoid
non-interactive use of ‘sudo’. Rather, it's meant to be used
interactively by a real, present human with credentials to run the
command.

> If I run it from a python program it returns an empty string.

You can also check the exit status of a command; ‘grep’ will give
different exit status for a match versus no match.

> Below is the code I am using.  Suggestions
> appreciated.
>
> cmdlist = ["sudo", "grep", '"^$USER\:"', "/etc/shadow"]

One immediate difference I see is that you specify different arguments
to ‘grep’. You have a different pattern for each command.

* The ‘^user\:’ pattern matches “user\:” at the start of a line.

* The ‘^$USER\:’ pattern I think won't match anything, since “$” matches
  end-of-line and then you expect further characters *past* the end of
  the line. I think that will always fail to match any line.

> p = subprocess.Popen(cmdlist,
>                      stdout=subprocess.PIPE,
>                      stderr=subprocess.PIPE)
> shadow, err = p.communicate()

Maybe you are expecting Bash to be involved somehow (and so “$USER” will
be substituted by Bash with some other value). That's not what happens.

Instead, the ‘subprocess.Popen.communicate’ method will invoke the
program directly, without involving a shell. See the documentation for
‘subprocess.Popen’.

-- 
 \            “If you continue running Windows, your system may become |
  `\        unstable.” —Microsoft, Windows 95 bluescreen error message |
_o__)                                                                  |
Ben Finney




More information about the Python-list mailing list