web-dev-qa-db-fra.com

Implémenter un shell interactif sur SSH en Python avec Paramiko?

Je veux écrire un programme (sous Python 3.x sous Windows 7) qui exécute plusieurs commandes sur un shell distant via ssh. Après avoir examiné la fonction exec_command () de paramikos, je me suis rendu compte que ce n'était pas adapté à mon cas d'utilisation (car le canal se ferme après l'exécution de la commande), car les commandes dépendent de variables d'environnement (définies par des commandes précédentes) et ne peuvent pas être concaténés en un seul appel exec_command () car ils doivent être exécutés à des moments différents du programme.

Ainsi, je veux exécuter des commandes sur le même canal. L'option suivante que j'ai envisagée consistait à implémenter un shell interactif à l'aide de la fonction invoke_Shell () de paramikos:

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() 

Il y a quelques problèmes avec ce code:

  1. La première "impression" n'imprime pas toujours la sortie "ls" (parfois, elle n'est imprimée que sur la deuxième "impression").
  2. Les premières commandes 'cd' et 'ls' sont toujours présentes dans la sortie (je les reçois via la commande 'recv', en tant que partie de la sortie), tandis que toutes les commandes 'cd' et 'ls' suivantes sont imprimées parfois, et parfois ils ne sont pas.
  3. Les deuxième et troisième commandes "cd" et "ls" (lorsqu'elles sont imprimées) apparaissent toujours avant la première sortie "ls".

Je suis confondu avec ce "non-déterminisme" et j'apprécierais beaucoup votre aide.

13
misha
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
14
misha

J'utilise fondamentalement tout votre code et je viens d'ajouter une boucle for:

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

Il exécute 2 des commandes avec la sortie correcte, mais reste alors là. Je me demande si je dois appeler del quelque part dans le code. 

0
magicsword