Subprocess Module

Synopsis

Python's subprocess module makes it easy to invoke external commands on localhost. It can also use SSH to invoke commands on remote hosts.

How simple?

The short answer is, very simple!

The longer answer is, subprocess.run, as follows:

1
2
3
4
5
result = subprocess.run(["ssh", "some-host@some-user", "some-command"],
                        shell=False,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        check=False)

Prerequisites

For subprocess ssh-run to work, ssh configuration needs to be in place. This means that:

  1. ssh-keygen on originating machine

  2. ssh-copy-id to the target machine into the target account's .ssh/authorized_keys

  3. optional: create .ssh/config for easier ssh call

  4. optional: use command in target .ssh/authorized_keys to allow proper setup, if needed, e.g:

    1
    command="if [[ \"x${SSH_ORIGINAL_COMMAND}x\" != \"xx\" ]]; then source ~/.profile; eval \"${SSH_ORIGINAL_COMMAND}\"; else /bin/bash --login; fi;" <ssh_key>
    

Note that if you use bash, .profile may be .bash_profile. If you don't use bash, an appropriate shell profile should be sourced.

Wrapper

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def sshcmd(host, command, user=None, stdin=None, check=False):
    ''' Runs ssh command via subprocess.  Assuming .ssh/config is configured.

    Args:
        host: target host to send the command to
        command: command to run on the host
        user: (optional) user to use to login to host
        stdin: (optional) override sys.stdin
        check: (optional) pass to *subprocess.run*; if set, checks return code
            and raises subprocess.CalledProcessError, if none-zero result

    Returns:
        subprocess.CompletedProcess object
    '''

    where = "%s" % host if user is None else "%s@%s" %(user, host)
    result = subprocess.run(["ssh", where, command],
                           shell=False,
                           stdin=stdin,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           check=check)
    return result

if __name__ == '__main__':
    # will work on if you have a host named ubly with ssh configuration
    out = sshcmd("ubly", "ls -l", check=False).stdout.decode()

Note the following:

  1. check=True would cause subprocess.run to through subprocess.CalledProcessError, if ssh exits with error code (greater than 0).
  2. sshcmd returns subprocess.CompletedProcess; which gives full access to PIPEs and return-code.
  3. stdout and stderr attributes are of binary form. Therefore result needs to be decoded().

References

source code: sshpipe

Share Post:

LinkedinDiaspora*TwitterFacebookGoogle+Email

Comments


There are no comments yet.

Add a Comment

You can use the Markdown syntax to format your comment.