2016-03-05 19 views
6

Voglio scrivere un programma (in Python 3.x su Windows 7) che esegue più comandi su una shell remota tramite ssh. Dopo aver esaminato la funzione exec_command() di paramikos, ho realizzato che non è adatto al mio caso d'uso (perché il canale si chiude dopo l'esecuzione del comando), poiché i comandi dipendono dalle variabili d'ambiente (impostate dai comandi precedenti) e non possono essere concatenato in una chiamata exec_command() in quanto devono essere eseguiti in momenti diversi del programma.Implementare una shell interattiva su ssh in Python usando Paramiko?

Pertanto, voglio eseguire comandi nello stesso canale. L'opzione successiva ho guardato in stato attuando una shell interattiva utilizzando paramikos' invoke_shell() funzione:

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

Ci sono alcuni problemi con questo codice:

  1. La prima 'di stampa' non sempre la stampa l'output "ls" (a volte viene stampato solo sulla seconda "stampa").
  2. I primi comandi 'cd' e 'ls' sono sempre presenti nell'output (li ottengo tramite il comando 'recv', come parte dell'output), mentre tutti i seguenti comandi 'cd' e 'ls' sono stampato a volte, e talvolta non lo sono.
  3. Il secondo e il terzo comando "cd" e "ls" (quando stampati) appaiono sempre prima del primo output "ls".

Sono confuso con questo "non-determinismo" e apprezzerei molto il vostro aiuto.

+0

avrai più aiuto se sostituisci il tag con il minor numero di follower con un tag python, assumendo che si tratti di un codice Python. in bocca al lupo. – shellter

+0

Devi usare 'paramiko'? Ho trovato molto più facile lavorare con 'fabric'. Hai appena impostato le variabili 'env' come' utente', 'password' e' host_string' e poi puoi fare varie cose come usare: 'get' per scaricare file dall'host remoto,' put' per inviare file e 'run' per emettere comandi. Puoi concatenare comandi come questo ad esempio: 'run ('cd .. && cd simulator && ls')'. – kchomski

+0

@kchomski sfortunatamente fabric non è compatibile con python 3.x quindi non è un'opzione. Ad ogni modo, da quello che ho visto, Fabric è solo un wrapper per paramiko e non mi permette di eseguire comandi "non concatenati" nello stesso canale. C'è un sacco di logica che alla fine voglio eseguire tra i comandi della shell. – misha

risposta

8
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 
+0

Come posso inviare più comandi a execute()? Ho provato a fare un ciclo for: per il comando in comandi: object.execute (comando) per un elenco di comandi, ma esegue solo 2 comandi, quindi devo riavviare la shell. – magicsword

+0

Puoi mostrarmi il codice? – misha

+0

Cosa succede se il mio comando produce sia stdout che stderr e li voglio come file separati? –

0

praticamente sto usando tutto il codice e solo l'aggiunta di un ciclo for:

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

Esegue 2 dei comandi con l'output corretto, ma poi appena ci si siede. Mi chiedo se devo chiamare lo del da qualche parte nel codice.

+0

Potrebbe essere che il terzo comando richieda un input interattivo da parte dell'utente? Quali comandi stai cercando di eseguire? – misha

+0

Sembra che non ritorni. Se ho dentro, fuori, err = conn_one.execute (comando) print (in, out, err) Non ottengo nulla indietro. Dovrei chiamare ssh.close() nel codice per tornare al ciclo? – magicsword

+0

No, non dovresti chiamare close(). Potresti dirmi esattamente i comandi che stai cercando di eseguire? – misha

Problemi correlati