3

I use the Paramiko Python package to run commands over SSH. I can get output to stdout but how can I properly get stdout, stderr and stdin redirected to the sys ones?

The code below would only display "stdout1" and "stdout2" on stdout. How do I get "stderr" in there properly? And preferably also support for stdin?

import paramiko
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.connect("localhost")
stdin, stdout, stderr = ssh.exec_command("echo stdout1 >&1; echo stderr >&2; echo stdout2 >&1")
ch = stdout.channel
while True:
    bs = ch.recv(1)
    if not bs:
        break
    print(bs.decode("utf-8"), end="")
Per Appelgren
  • 57
  • 1
  • 5

2 Answers2

2

You may read lines from ChannelFile (http://docs.paramiko.org/en/2.4/api/channel.html?highlight=stdout#paramiko.channel.ChannelFile).

Example:

stdin, stdout, stderr = ssh.exec_command("echo stdout1 >&1; echo stderr >&2; echo stdout2 >&1")

while True:
    print(stdout.read().decode(), end='')
    if stdout.channel.exit_status_ready():
        break

while True:
    print(stderr.read().decode(), end='')
    if stderr.channel.exit_status_ready():
        break

This solution has some drawbacks well described in: Paramiko recv()/read()/readline(s)() on stderr returns empty string.

gbajson
  • 1,531
  • 13
  • 32
  • 1
    The problem with that solution was that "stdout2" is printed before "stderr". I found an alternative package called asyncssh which was easier to use for my use case. – Per Appelgren Apr 19 '19 at 18:02
1

I found asyncssh which was a better fit for me. The following code works as expected:

import asyncssh
import asyncio
import sys

async def run():
    async with asyncssh.connect('localhost') as ssh:
        await ssh.run("echo stdout1 >&1; echo stderr >&2; echo stdout2 >&1",
                      stdout=sys.stdout,
                      stderr=sys.stderr)

asyncio.run(run())
Per Appelgren
  • 57
  • 1
  • 5