Search code examples
pythonpython-3.xsshremote-accessscp

SSH to remote system and run python script and get output


I am playing with ssh and run the python scripts with help of these answers - Run local python script on remote server

For connecting the server using ssh, I am currently using the subprocess from python

#ssh command : ssh user@machine python < script.py - arg1 arg2

output = subprocess.Popen(f"ssh user@machine python3 < script.py", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

Now, I get the output of the script as tuples of bytes of string, it is hard to decode the output information. The output having other information like warnings.

I tried decoding the output, but that is not looks great,

Is there any other possible ways to get the output back from ssh, for example, the script.py print a python dictionary or returns a json data, which can get it back in the same format and save in the output variable?

Thanks


Solution

  • There are a couple of ways to do this with subprocess depending on how up to date your Python is.

    In general any byte string in Python you can turn into 'proper' text by decoding it. In your example, the command is returning a tuple which is the output of (stdout, stderr) so we need to pick one of those tuple elements with output[0] or output[1]:

    >>> output = subprocess.Popen(f"ssh git@github.com", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    >>> output[1].decode('utf-8')
    "PTY allocation request failed on channel 0\r\nHi Chris! You've successfully authenticated, but GitHub does not provide shell access.\nConnection to github.com closed.\r\n"
    

    You can tidy that up further by including the universal_newlines=True option.

    Assuming you are Python 3.7 or later you can simplify your command so you capture a decoded output straight away:

    >>> subprocess.run(['ssh', 'git@github.com'], capture_output=True, text=True).stderr
    "PTY allocation request failed on channel 0\nHi Chris! You've successfully authenticated, but GitHub does not provide shell access.\nConnection to github.com closed.\n"
    

    If you are struggling with the escaped newlines (\n) one approach would be to split them so you get an array of lines instead:

    >>> output = subprocess.run(['ssh', 'git@github.com'], capture_output=True, text=True, universal_newlines=True).stderr.split("\n")
    
    >>> output
    ['PTY allocation request failed on channel 0', "Hi Chris! You've successfully authenticated, but GitHub does not provide shell access.", 'Connection to github.com closed.', '']
    
    >>> for line in output:
    ...     print(line)
    
    PTY allocation request failed on channel 0
    Hi Chris! You've successfully authenticated, but GitHub does not provide shell access.
    Connection to github.com closed.
    

    print() will happily accept an array of terms to print, normally separated by a space. But you can override the separator:

    print(*output, sep="\n")