13

I use Paramiko to run some ssh commands to the remote Linux server. The commands will have continuous output in the console and I want to print these all information in the local console window.

stdin, stdout, stderr = ssh.client.exec_command("ls")
for line in stdout.read()
    print line,
ssh.client.close()

So if I write the code like this, all the output information will be sent back to me until the command finishes executing while I want to print the output in live.

Thanks a lot.

2 Answers 2

19

Of course there is a way to do this. Paramiko exec_command is async, buffers are filled while data arrives regardless of your main thread.

In your example stdout.read(size=None) will try to read the full buffer size at once. Since new data is always arriving, it won't ever exit. To avoid this, you could just try to read from stdout in smaller chunks. Here's an example that reads buffers bytewise and yields lines once a \n is received.

stdin,stdout,stderr = ssh.exec_command("while true; do uptime; done")

def line_buffered(f):
    line_buf = ""
    while not f.channel.exit_status_ready():
        line_buf += f.read(1)
        if line_buf.endswith('\n'):
            yield line_buf
            line_buf = ''

for l in line_buffered(stdout):
    print l

You can increase performance by tweaking the code to use select.select() and by having bigger chunk sizes, see this answer that also takes into account common hang and remote command exit detection scenarios that may lead to empty responses.

Sign up to request clarification or add additional context in comments.

4 Comments

The answer is good. But you also need to cater for the stderr, otherwise the code might deadlock. See Paramiko ssh die/hang with big output.
Btw, wouldn't this simple code do? while True: line = f.readline() if len(line) == 0: break print(line) Or even simple for line in stdout: print(line)
For python3 I had to make all strings byte-strings b"" and then .decode("utf-8") while printing. If someone would elaborate on increasing the performance I would be glad
this answer did not solve the problem in my case, I had to use this one: stackoverflow.com/questions/55642555/…
-1

So because sharing is caring , I resolved the issue for the PARAMIKO with continuous output like this (ignore the index and replace stuff, issues proprietary to my device):

"elif action == "icmd":
        cmd = command
        channel = ssh.get_transport().open_session()
        channel.exec_command(cmd)
        channel.set_combine_stderr(True)
        index = 0
        a = ""  # list to be filled with output of the commands.
        while True:
            if channel.exit_status_ready():
                break
            o = channel.recv(2048).decode("utf-8")
            if o:
                a += o
                if "\n" in o:
                    b = a.split("\n")
                    a = b[-1]
                    b.pop()
                    for x in b:
                        print(x.replace("●", "").replace("└─", ""), flush=True)
                        index += 1
                if index == 0 and len(o) > 0:
                    print(o.replace("●", "").replace("└─", ""), flush=True)
                if index == 0 and len(o) == 0:
                    print(f"Your command '{cmd}' did not returned anything! ")
        channel.close()
        ssh.close()

"

I chose the split and pop things because when using commands like : traceroute www.google.com , for example, the output was splited into separate rows.

Also, I integrated a shell, for interactive input/output with the device, like this :

elif action == "shell":
        channel = ssh.invoke_shell()
        cmd = command
        print("Shell started")
        print(channel.recv(2048).decode())
        while True:
            if channel.exit_status_ready():
                print("Session Ended!")
                break
            try:
                cmd = input(cmd)
                channel.send(f"{cmd}\n".encode())
                time.sleep(1)
                output = channel.recv(2048).decode("utf-8")
                print(output.replace("●", "").replace("└─", ""), flush=True)
                if cmd == "exit":
                    break
            except KeyboardInterrupt:
                break
        channel.close()
        ssh.close()

Enjoy it !

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.