Comment obtenir le code retour SSH en utilisant Paramiko?

97
client = paramiko.SSHClient()
stdin, stdout, stderr = client.exec_command(command)

Existe-t-il un moyen d'obtenir le code de retour de la commande?

Il est difficile d'analyser tous les stdout / stderr et de savoir si la commande s'est terminée avec succès ou non.

Beyonder
la source

Réponses:

51

SSHClient est une classe wrapper simple autour des fonctionnalités de niveau inférieur de Paramiko. La documentation de l'API répertorie une recv_exit_status()méthode sur la Channelclasse.

Un script de démonstration très simple:

import paramiko
import getpass

pw = getpass.getpass()

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect('127.0.0.1', password=pw)

while True:
    cmd = raw_input("Command to run: ")
    if cmd == "":
        break
    chan = client.get_transport().open_session()
    print "running '%s'" % cmd
    chan.exec_command(cmd)
    print "exit status: %s" % chan.recv_exit_status()

client.close()

Exemple de son exécution:

$ python sshtest.py
Password: 
Command to run: true
running 'true'
exit status: 0
Command to run: false
running 'false'
exit status: 1
Command to run: 
$
JanC
la source
9
C'est une mauvaise solution. Voir la réponse de @apdastous ci-dessous, c'est beaucoup mieux.
Patrick
Tant que vous avez raison recv_exit_status, vous ne pouvez pas l'utiliser de cette façon, car le code peut se bloquer. Vous devez consommer la sortie de la commande en attendant la fin de la commande. Voir Paramiko ssh die / hang avec une grande sortie .
Martin Prikryl
283

Un exemple beaucoup plus simple qui n'implique pas d'appeler directement la classe de canal "de niveau inférieur" (c'est-à-dire - PAS en utilisant la client.get_transport().open_session()commande):

import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('blahblah.com')

stdin, stdout, stderr = client.exec_command("uptime")
print stdout.channel.recv_exit_status()    # status is 0

stdin, stdout, stderr = client.exec_command("oauwhduawhd")
print stdout.channel.recv_exit_status()    # status is 127
apeuré
la source
5
Ce qui est bien avec cet exemple, c'est de capturer non seulement EXIT (comme la question le demande), mais aussi de démontrer que vous pouvez toujours obtenir STDOUT et STDERR. Il y a des années, j'ai été induit en erreur par l'exemple de base de code anémique de Paramiko (sans manque de respect), et pour obtenir EXIT, j'ai eu recours à des appels de transport () de bas niveau. transport () a semblé vous forcer à "en choisir un" (ce qui n'est peut-être pas vrai, mais le manque d'exemples et de didacticiels m'a conduit à le croire) ...
Scott Prive
Tant que vous avez raison recv_exit_status, vous ne pouvez pas l'utiliser de cette façon, car le code peut se bloquer. Vous devez consommer la sortie de la commande en attendant la fin de la commande. Voir Paramiko ssh die / hang avec une grande sortie .
Martin Prikryl
5

Merci pour JanC, j'ai ajouté quelques modifications pour l'exemple et testé en Python3, c'est vraiment utile pour moi.

import paramiko
import getpass

pw = getpass.getpass()

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
#client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

def start():
    try :
        client.connect('127.0.0.1', port=22, username='ubuntu', password=pw)
        return True
    except Exception as e:
        #client.close()
        print(e)
        return False

while start():
    key = True
    cmd = input("Command to run: ")
    if cmd == "":
        break
    chan = client.get_transport().open_session()
    print("running '%s'" % cmd)
    chan.exec_command(cmd)
    while key:
        if chan.recv_ready():
            print("recv:\n%s" % chan.recv(4096).decode('ascii'))
        if chan.recv_stderr_ready():
            print("error:\n%s" % chan.recv_stderr(4096).decode('ascii'))
        if chan.exit_status_ready():
            print("exit status: %s" % chan.recv_exit_status())
            key = False
            client.close()
client.close()
périlleux
la source
obtenons-nous un message d'erreur -s'il y en a - dans chan.recv_stderr_ready () ?. Est-ce que chan.recv_stderr (4096) .decode ('ascii') renvoie un texte de message?
kten
0

Dans mon cas, la mise en mémoire tampon de sortie était le problème. En raison de la mise en mémoire tampon, les sorties de l'application ne sortent pas de manière non bloquante. Vous pouvez trouver la réponse sur la façon d'imprimer la sortie sans mise en mémoire tampon ici: Désactivez la mise en mémoire tampon de sortie . Pour faire court, lancez simplement python avec l'option -u comme ceci:

> python -u script.py

Youngmin Kim
la source