运行 带有参数的脚本,通过 ssh 和来自 python 脚本的 at 命令
Run script with arguments via ssh with at command from python script
我有一个 python 程序需要通过 ssh 调用远程系统上的脚本。
此 ssh 调用需要在指定日期发生(一次),这可以通过 linux at 命令完成。
我可以使用我的 python 程序中的 os
模块或 subprocess
模块调用这两个外部 bash 命令。将某些参数传递给远程脚本时会出现问题。
除了被远程 运行 和在以后的某个日期,我想调用的 (bash) 脚本需要传递几个参数给它,这些参数是 python 我希望传递给脚本的变量。
user="user@remote"
arg1="argument with spaces"
arg2="two"
cmd="ssh "+user+"' /home/user/path/script.sh "+arg1+" "+arg2+"'"
os.system(cmd)
其中一个参数是包含空格的字符串,但最好作为单个参数传递;
例如:
./script.sh "Argument with Spaces"
其中 $1 等于 "Argument with Spaces"
我尝试了在 python 和字符串本身中转义双引号和单引号的各种组合,以及在整个 ssh 命令周围使用重音符号。最成功的版本会根据需要使用参数调用脚本,但会立即忽略 at 命令和 运行s。
在 python 中是否有一个干净的方法来完成这个?
新答案
既然您已经编辑了您的问题,您应该可以使用格式字符串
cmd = '''ssh {user} "{cmd} '{arg0}' '{arg1}'"'''.format(user="user@remote",cmd="somescript",arg0="hello",arg2="hello world")
print cmd
旧答案
我想你可以使用 -c
开关和 ssh
在远程机器上执行一些代码 (ssh user@host.net -c "python myscript.py arg1 arg2"
)
或者我需要更多,所以我使用这个 paramiko 包装器 class(你需要安装 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()
然后您可以按如下方式使用它
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
我有一个 python 程序需要通过 ssh 调用远程系统上的脚本。
此 ssh 调用需要在指定日期发生(一次),这可以通过 linux at 命令完成。
我可以使用我的 python 程序中的 os
模块或 subprocess
模块调用这两个外部 bash 命令。将某些参数传递给远程脚本时会出现问题。
除了被远程 运行 和在以后的某个日期,我想调用的 (bash) 脚本需要传递几个参数给它,这些参数是 python 我希望传递给脚本的变量。
user="user@remote"
arg1="argument with spaces"
arg2="two"
cmd="ssh "+user+"' /home/user/path/script.sh "+arg1+" "+arg2+"'"
os.system(cmd)
其中一个参数是包含空格的字符串,但最好作为单个参数传递;
例如:
./script.sh "Argument with Spaces"
其中 $1 等于 "Argument with Spaces"
我尝试了在 python 和字符串本身中转义双引号和单引号的各种组合,以及在整个 ssh 命令周围使用重音符号。最成功的版本会根据需要使用参数调用脚本,但会立即忽略 at 命令和 运行s。
在 python 中是否有一个干净的方法来完成这个?
新答案
既然您已经编辑了您的问题,您应该可以使用格式字符串
cmd = '''ssh {user} "{cmd} '{arg0}' '{arg1}'"'''.format(user="user@remote",cmd="somescript",arg0="hello",arg2="hello world")
print cmd
旧答案
我想你可以使用 -c
开关和 ssh
在远程机器上执行一些代码 (ssh user@host.net -c "python myscript.py arg1 arg2"
)
或者我需要更多,所以我使用这个 paramiko 包装器 class(你需要安装 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()
然后您可以按如下方式使用它
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