Embed an interactive shell on top of ssh in Python using Paramiko?

I want to write a program (in Python 3.x on Windows 7) that executes several commands on a remote shell via ssh. Looking at the exec_command() function, I realized that it is not suitable for my exec_command() use (since the channel closes after the command is executed), because the commands depend on environment variables (set by previous commands) and cannot be combined into one exec_command() since they must be executed at different times in the program.

Thus, I want to execute commands in one channel. The next option I learned was to implement an interactive shell using the invoke_shell() function:

 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(host, username=user, password=psw, port=22) channel = ssh.invoke_shell() out = channel.recv(9999) channel.send('cd mivne_final\n') channel.send('ls\n') while not channel.recv_ready(): time.sleep(3) out = channel.recv(9999) print(out.decode("ascii")) channel.send('cd ..\n') channel.send('cd or_fail\n') channel.send('ls\n') while not channel.recv_ready(): time.sleep(3) out = channel.recv(9999) print(out.decode("ascii")) channel.send('cd ..\n') channel.send('cd simulator\n') channel.send('ls\n') while not channel.recv_ready(): time.sleep(3) out = channel.recv(9999) print(out.decode("ascii")) ssh.close() 

There are some problems with this code:

  1. The first print does not always print the output of ls (sometimes it only prints on the second print ).
  2. The first cd and ls always present in the output (I get them through the recv command as part of the output), while all subsequent cd and ls sometimes printed, and sometimes not.
  3. The second and third commands cd and ls (when printed) always appear before the first output of ls .

I got confused with this β€œnon-determinism” and would greatly appreciate your help.

+14
source share
2 answers
 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 
+15
source

I mostly use all your code and just add a for loop:

 commands = ["ls","command2","command3"] conn_one = ShellHandler(host,name,pwd) for command in commands: conn_one.execute(command) 

He executes 2 commands with the correct exit, but then he just sits there. I am wondering if I need to call del somewhere in the code.

0
source

All Articles