Running Interactive Commands in Paramiko

Running interactive commands in Paramiko

The full paramiko distribution ships with a lot of good demos.

In the demos subdirectory, demo.py and interactive.py have full interactive TTY examples which would probably be overkill for your situation.

In your example above ssh_stdin acts like a standard Python file object, so ssh_stdin.write should work so long as the channel is still open.

I've never needed to write to stdin, but the docs suggest that a channel is closed as soon as a command exits, so using the standard stdin.write method to send a password up probably won't work. There are lower level paramiko commands on the channel itself that give you more control - see how the SSHClient.exec_command method is implemented for all the gory details.

Executing commands of interactive tool and reading their output with Paramiko

The command that you are executing is interactive. When started, it waits for subcommands. While you execute it and wait for the command to finish (by calling readlines). What never happens.

You have to feed the subcommands to your command to have it do something.

See Pass input/variables to command/script over SSH using Python Paramiko.

You will also have to make the command quit (by sending a subcommand like exit/quit/bye).

how to interact with Paramiko's interactive shell session?

I imported a file, interactive.py, found on Paramiko's GitHub. After importing it, I just had to change my code to this:

try:
import interactive
except ImportError:
from . import interactive

...
...

channel.invoke_shell()
interactive.interactive_shell(channel)
sshClient.close()

Combining interactive shell and recv_exit_status method using Paramiko

SSHClient.recv_exit_status signals that the channel has closed.

The "shell" channel closes only when the shell closes. A "shell" is a black box with input and output. Nothing else. There's no way SSH, let alone Paramiko, will be able to tell anyhow, when individual commands in the shell have finished. The shell_chan.send('something') is not a signal to SSH to execute a command. It simply sends an arbitrary input to the "shell". Nothing else. It's totally at the shell disposition how the input is interpreted. All that the SSH/Paramiko gets back is an arbitrary unstructured output. Paramiko cannot interpret it anyhow for you.


If you need to be able to tell when a command has finished, you need to use "exec" channel (SSHClient.exec_command method in Paramiko). You should not use the "shell" channel, unless you are implementing an interactive SSH terminal client, like PuTTY (or in rare cases, when you talk to an limited SSH server that does not implement the "exec" channel, as it often the case with dedicated devices, such as routers, switches, etc).


For related questions, see:

  • Execute multiple dependent commands individually with Paramiko and find out when each command finishes
  • How to get each dependent command execution output using Paramiko exec_command
  • Use the same SSH object to issue "exec_command()" multiple times in Paramiko
  • Execute (sub)commands in secondary shell/command on SSH server in Paramiko
  • Invoke multiple commands inside a process in interactive ssh mode
  • Executing command using Paramiko exec_command on device is not working
  • What is the difference between exec_command and send with invoke_shell() on Paramiko?

Implement an interactive shell over ssh in Python using Paramiko?

import paramiko
import re

class ShellHandler:

def __init__(self, host, user, psw):
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(host, username=user, password=psw, port=22)

channel = self.ssh.invoke_shell()
self.stdin = channel.makefile('wb')
self.stdout = channel.makefile('r')

def __del__(self):
self.ssh.close()

def execute(self, cmd):
"""

:param cmd: the command to be executed on the remote computer
:examples: execute('ls')
execute('finger')
execute('cd folder_name')
"""
cmd = cmd.strip('\n')
self.stdin.write(cmd + '\n')
finish = 'end of stdOUT buffer. finished with exit status'
echo_cmd = 'echo {} $?'.format(finish)
self.stdin.write(echo_cmd + '\n')
shin = self.stdin
self.stdin.flush()

shout = []
sherr = []
exit_status = 0
for line in self.stdout:
if str(line).startswith(cmd) or str(line).startswith(echo_cmd):
# up for now filled with shell junk from stdin
shout = []
elif str(line).startswith(finish):
# our finish command ends with the exit status
exit_status = int(str(line).rsplit(maxsplit=1)[1])
if exit_status:
# stderr is combined with stdout.
# thus, swap sherr with shout in a case of failure.
sherr = shout
shout = []
break
else:
# get rid of 'coloring and formatting' special characters
shout.append(re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]').sub('', line).
replace('\b', '').replace('\r', ''))

# first and last lines of shout/sherr contain a prompt
if shout and echo_cmd in shout[-1]:
shout.pop()
if shout and cmd in shout[0]:
shout.pop(0)
if sherr and echo_cmd in sherr[-1]:
sherr.pop()
if sherr and cmd in sherr[0]:
sherr.pop(0)

return shin, shout, sherr


Related Topics



Leave a reply



Submit