巧用Python登陆远程服务器( 三 )
断开连接
在使用完远程主机后,我们应该关闭与远程主机的连接 。不这样做不一定是灾难性的,但是我遇到过一些实例,其中足够的挂起连接最终会使端口22的入站流量达到最大 。不管您的用例是否认为重启是一场灾难或轻微的不便,让我们像成年人一样关闭我们该死的连接,就像我们在排便后擦屁股一样 。不管您的连接环境如何,我提倡设置一个超时变量(如前所述) 。无论如何 。瞧:
class RemoteClient:...def disconnect(self):"""Close ssh connection."""if self.client:self.client.close()if self.scp:self.scp.close()
【巧用Python登陆远程服务器】有趣的事实:设置self.client.close()实际上设置self 。将client设置为等于None,这在您可能希望检查连接是否已经打开的情况下非常有用 。
执行Unix命令
我们现在有了一个很棒的Python类,它可以找到RSA密钥、连接和断开连接 。它确实缺乏做任何有用的事情的能力 。
我们可以修复这个问题,并最终开始使用一个全新的方法来执行命令,我将适当地将其命名为execute_commands()(正确地说,“命令”可能不止一个,我们稍后将讨论这个问题) 。所有这些工作都是由Paramiko客户端内置的exec_command()方法完成的,它接受一个字符串作为命令并执行它:
class RemoteClient:...def execute_commands(self, commands):"""Execute multiple commands in succession.:param commands: List of unix commands as strings.:type commands: List[str]"""self.conn = self._connect()for cmd in commands:stdin, stdout, stderr = self.client.exec_command(cmd)stdout.channel.recv_exit_status()response = stdout.readlines()for line in response:logger.info(f'INPUT: {cmd} | OUTPUT: {line}')
我们刚刚创建的函数execute_commands()期望一个字符串列表作为命令执行 。这部分是为了方便,但也因为Paramiko不会在命令之间运行任何“状态”更改(比如更改目录),所以我们传递给Paramiko的每个命令都应该假定我们是在服务器的根目录下工作的 。我冒昧地说出了这样三条命令:
remote.execute_commands(['cd /var/www/ && ls','tail /var/log/Nginx/access.log','ps aux | grep node'])
我可以通过将cd path/链接到/dir && ls来查看一个目录的内容,但是运行cd path/to/dir后跟着ls会导致空无,因为ls第二次返回服务器根目录下的文件列表 。
通过SCP上传(下载)文件
SCP既指用于将文件复制到远程计算机的协议(安全复制协议),也指利用此协议的Python库 。我们已经安装了SCP库,所以请导入它 。
SCP和Paramiko库相互补充,使得通过SCP上传非常容易 。SCPClient()创建一个期望Paramiko进行“传输”的对象,我们通过self.conn.get_transport()提供了这个对象 。从语法上讲,创建SCP连接依赖于我们的SSH客户机,但这些连接是独立的 。关闭SSH连接而保持SCP连接打开是可能的,所以不要这样做 。像这样打开一个SCP连接:
self.scp = SCPClient(self.client.get_transport())
上传单个文件很无聊,所以让我们来上传整个目录的文件 。Bulk_upload()接受文件路径列表,然后调用_upload_single_file()
class RemoteClient:...def bulk_upload(self, files):"""Upload multiple files to a remote directory.:param files: List of paths to local files.:type files: List[str]"""self.conn = self._connect()uploads = [self._upload_single_file(file) for file in files]logger.info(f'Finished uploading {len(uploads)} files to {self.remote_path} on {self.host}')def _upload_single_file(self, file):"""Upload a single file to a remote directory."""upload = Nonetry:self.scp.put(file,recursive=True,remote_path=self.remote_path)upload = fileexcept SCPException as error:logger.error(error)raise errorfinally:logger.info(f'Uploaded {file} to {self.remote_path}')return upload
我们的方法期望接收两个字符串:第一个是文件的本地路径,第二个是我们想要上传的远程目录的路径 。
SCP的put()方法将把一个本地文件上传到远程主机 。如果现有的文件恰好存在于我们指定的目标上,这将用相同的名称替换它们 。这就是所有需要的!
下载文件
与SCP的put()对应的是get()方法:
class RemoteClient:...def download_file(self, file):"""Download file from remote host."""self.conn = self._connect()self.scp.get(file)
推荐阅读
- Python重要知识,生成器的威力
- 怎么用python批量获取免费代理IP
- python怎么验证代理IP是否有效
- 终于把所有的 Python 库都整理出来啦
- 太强了,用Python制作动态可视化图表
- 一篇文章带你搞懂Python中的类
- 这两个Python工具真香!修改代码不会影响运行
- 如何用Python输出数学公式?
- Python实现用手机监控远程控制电脑
- Pythonic风格代码有什么好处?附12个代码实例