简体   繁体   English

使用python脚本中的at命令通过ssh通过参数运行脚本

[英]Run script with arguments via ssh with at command from python script

I have a python program which needs to call a script on a remote system via ssh. 我有一个python程序,需要通过ssh调用远程系统上的脚本。

This ssh call needs to happen (once) at a specified date which can be done via the linux at command. ssh调用需要在指定的日期(一次)发生,这可以通过linux at命令来完成。

I am able to call both of these external bash commands using either the os module or the subprocess module from my python program. 我可以使用os模块或python程序中的subprocess模块调用这两个外部bash命令。 The issue comes when passing certain arguments to the remote script. 将某些参数传递给远程脚本时会出现问题。

In addition to being run remotely and at a later date, the (bash) script I wish to call requires several arguments to be passed to it, these arguments are python variables which I wish to pass on to the script. 除了要在以后远程运行之外,我希望调用的(bash)脚本还需要传递几个参数,这些参数是我希望传递给脚本的python变量。

user="user@remote"
arg1="argument with spaces"
arg2="two"
cmd="ssh "+user+"' /home/user/path/script.sh "+arg1+" "+arg2+"'" 
os.system(cmd)

One of these arguments is a string which contains spaces but would ideally be passed as a single argument; 这些参数之一是一个字符串,其中包含空格,但理想情况下将作为单个参数传递。

for example: 例如:

./script.sh "Argument with Spaces" where $1 is equal to "Argument with Spaces" ./script.sh "Argument with Spaces" ,其中$ 1等于"Argument with Spaces"

I have tried various combinations of escaping double and single quotes in both python and the string itself and the use of grave accents around the entire ssh command. 我尝试了在python和字符串本身中转义双引号和单引号的各种组合,以及在整个ssh命令周围使用重音符号的尝试。 The most successful version calls the script with the arguments as desired, but ignores the at command and runs immediately. 最成功的版本根据需要调用带有参数的脚本,但是会忽略at命令并立即运行。

Is there a clean way within python to accomplish this? python中是否有一种干净的方法可以完成此任务?

new answer 新答案

now that you edited your question you should probably be using format strings 现在,您编辑了问题,您可能应该使用格式字符串

cmd = '''ssh {user} "{cmd} '{arg0}' '{arg1}'"'''.format(user="user@remote",cmd="somescript",arg0="hello",arg2="hello world")
print cmd

old answer 旧答案

I think you can use a -c switch with ssh to execute some code on a remote machine ( ssh user@host.net -c "python myscript.py arg1 arg2" ) 我认为您可以在ssh上使用-c开关在远程计算机上执行一些代码( ssh user@host.net -c "python myscript.py arg1 arg2"

alternatively I needed more than that so I use this paramiko wrapper class (you will need to install paramiko) 另外,我还需要更多,所以我使用此paramiko包装器类(您将需要安装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:pass@ip'")
        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()

you can then use it as follows 然后可以按以下方式使用它

client = SshClient("username:password@host.net")
result = client.execute("python something.py cmd1 cmd2")
print result

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM