2016-07-26 3 views
0

J'ai un programme python qui doit appeler un script sur un système distant via ssh.Exécuter un script avec des arguments via ssh avec une commande à partir du script python

Cet appel SSH doit se produire (une fois) à une date spécifiée, ce qui peut être fait via la commande linux at.

Je peux appeler ces deux commandes bash externes en utilisant le module os ou le module subprocess de mon programme python. Le problème survient lors de la transmission de certains arguments au script distant.

En plus d'être exécuté à distance et à une date ultérieure, le script (bash) que je souhaite appeler nécessite plusieurs arguments à lui passer, ces arguments sont des variables python que je souhaite transmettre au script.

user="[email protected]" 
arg1="argument with spaces" 
arg2="two" 
cmd="ssh "+user+"' /home/user/path/script.sh "+arg1+" "+arg2+"'" 
os.system(cmd) 

L'un de ces arguments est une chaîne qui contient des espaces, mais devrait idéalement être passé comme un seul argument;

par exemple:

./script.sh "Argument with Spaces" où 1 $ est égal à "Argument with Spaces"

J'ai essayé différentes combinaisons d'échapper à guillemets doubles et simples dans les deux python et la chaîne elle-même et l'utilisation des accents graves sur tout le commande ssh. La version la plus réussie appelle le script avec les arguments souhaités, mais ignore la commande at et s'exécute immédiatement.

Existe-t-il un moyen propre dans python pour accomplir ceci?

Répondre

2

nouvelle réponse

maintenant que vous avez modifié votre question, vous devriez probablement utiliser les chaînes de format

cmd = '''ssh {user} "{cmd} '{arg0}' '{arg1}'"'''.format(user="[email protected]",cmd="somescript",arg0="hello",arg2="hello world") 
print cmd 

ancienne réponse

Je pense que vous pouvez utiliser un -c passer à ssh pour exécuter du code sur une machine distante (ssh [email protected] -c "python myscript.py arg1 arg2")

encore je besoin de plus que si j'utilise cette classe wrapper paramiko (vous devez installer paramiko)

from contextlib import contextmanager 
import os 
import re 
import paramiko 
import time 


class SshClient: 
    """A wrapper of paramiko.SSHClient""" 
    TIMEOUT = 10 

    def __init__(self, connection_string,**kwargs): 
     self.key = kwargs.pop("key",None) 
     self.client = kwargs.pop("client",None) 
     self.connection_string = connection_string 
     try: 
      self.username,self.password,self.host = re.search("(\w+):(\w+)@(.*)",connection_string).groups() 
     except (TypeError,ValueError): 
      raise Exception("Invalid connection sting should be 'user:[email protected]'") 
     try: 
      self.host,self.port = self.host.split(":",1) 
     except (TypeError,ValueError): 
      self.port = "22" 
     self.connect(self.host,int(self.port),self.username,self.password,self.key) 
    def reconnect(self): 
     self.connect(self.host,int(self.port),self.username,self.password,self.key) 

    def connect(self, host, port, username, password, key=None): 
     self.client = paramiko.SSHClient() 
     self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
     self.client.connect(host, port, username=username, password=password, pkey=key, timeout=self.TIMEOUT) 

    def close(self): 
     if self.client is not None: 
      self.client.close() 
      self.client = None 

    def execute(self, command, sudo=False,**kwargs): 
     should_close=False 
     if not self.is_connected(): 
      self.reconnect() 
      should_close = True 
     feed_password = False 
     if sudo and self.username != "root": 
      command = "sudo -S -p '' %s" % command 
      feed_password = self.password is not None and len(self.password) > 0 
     stdin, stdout, stderr = self.client.exec_command(command,**kwargs) 
     if feed_password: 
      stdin.write(self.password + "\n") 
      stdin.flush() 

     result = {'out': stdout.readlines(), 
       'err': stderr.readlines(), 
       'retval': stdout.channel.recv_exit_status()} 
     if should_close: 
      self.close() 
     return result 

    @contextmanager 
    def _get_sftp(self): 
     yield paramiko.SFTPClient.from_transport(self.client.get_transport()) 

    def put_in_dir(self, src, dst): 
     if not isinstance(src,(list,tuple)): 
      src = [src] 
     print self.execute('''python -c "import os;os.makedirs('%s')"'''%dst) 
     with self._get_sftp() as sftp: 
      for s in src: 
       sftp.put(s, dst+os.path.basename(s)) 

    def get(self, src, dst): 
     with self._get_sftp() as sftp: 
      sftp.get(src, dst) 
    def rm(self,*remote_paths): 
     for p in remote_paths: 
      self.execute("rm -rf {0}".format(p),sudo=True) 
    def mkdir(self,dirname): 
     print self.execute("mkdir {0}".format(dirname)) 
    def remote_open(self,remote_file_path,open_mode): 
     with self._get_sftp() as sftp: 
      return sftp.open(remote_file_path,open_mode) 

    def is_connected(self): 
     transport = self.client.get_transport() if self.client else None 
     return transport and transport.is_active() 

vous pouvez alors l'utiliser comme suit

client = SshClient("username:[email protected]") 
result = client.execute("python something.py cmd1 cmd2") 
print result 

result2 = client.execute("cp some_file /etc/some_file",sudo=True) 
print result2