用ruby的net/ssh链接远程的服务器
最近用SSH比较多了,需要连接到一些远程的服务器上,然后执行一些命令。次数多了,渐渐就觉得比较麻烦。于是写了一个脚本用来让程序自动去执行一些重复的动作。
在ruby下的SSH库最常用的是net/ssh。可以从它的网站去找一些信息。(http://net-ssh.rubyforge.org/)上面有一些例子,来告诉用户到底怎么使用这个东西。其实模式很简单,先是通过start方法链接到服务器,开启一个session,然后在这个session的上下文上去用exec方法执行一些命令。当然还有更复杂一些的用法,不过鉴于需求只是执行命令,那么复杂的东西就放一放。
如果你愿意,每写一个脚本都用Net::SSH.start(...)开头可以,不过这个看上去并不方便。至少打的字还是比较多的。那就封装一下吧。其实怎么对这些库进行封装是仁者见仁智者见智的事情。我的想法是抽象出2个概念,配置(config)和一批命令(batch)。由此对于这个封装的用法自然就可以想到:
2 batch = config.create_batch
3 batch << 'ls'
4 batch << 'ps -Al'
5 puts batch.run
有了这个蓝图,剩下的就是照着这个蓝图写代码了。首先SSHConfig就是用来保存服务器的配置,并且创建一个Batch对象。当然,通过前面看过net/ssh例子,可以想起来其实SSHConfig应该还有一个功能就是根据这些配置创建一个session。

2 attr_accessor :user
3 attr_accessor :host
4 attr_accessor :password
5
6 def initialize(args = {})
7 @user = args[:user] unless args[:user].nil?
8 @host = args[:host] unless args[:host].nil?
9 @password = args[:password] unless args[:password].nil?
10 end
11
12 def create_session
13 Net::SSH.start(host, user, :password => password) do |ssh|
14 sess = Session.new(ssh)
15 yield sess
16 end
17 end
18
19 def create_batch
20 Batch.new(self)
21 end
22 end
这里我还是把net/ssh的session又包装了一下,只暴露出了一些自己用到的方法。下面就是Session类的代码:

2 def initialize(ssh)
3 @ssh = ssh
4 @stdout = ""
5 @stderr = ""
6 end
7
8 def exec!(cmd)
9 @ssh.exec!(cmd) do |ch, stream, data|
10 if stream == :stdout
11 stdout << data
12 elsif stream == :stderr
13 stderr << data
14 end
15 end
16 end
17
18 def stdout
19 @stdout
20 end
21
22 def stderr
23 @stderr
24 end
25 end
Session对象截获了通过它执行的命令所造成的输出,供命令执行过后为脚本打印结果使用。
最后的重头戏是Batch,他用来保存一系列的命令,然后执行他们。

2 def initialize(config, &block)
3 @config = config
4 @commands = []
5 @after_exec = block
6 end
7
8 def commands
9 @commands
10 end
11
12 def <<(item)
13 @commands << item
14 end
15
16 def add_command(cmd)
17 @commands << cmd
18 end
19
20 def run
21 ret = ""
22 @config.create_session do |sess|
23 @commands.each do |cmd|
24 sess.exec!(cmd)
25 end
26 unless @after_exec.nil?
27 @after_exec.call(sess)
28 end
29 ret = sess.stdout
30 end
31 ret
32 end
33 end
最后还有一个提示,就是通过SSH执行命令的话,两个不同的命令并不集成路径。也就是说如果先执行 cd ~\web, 然后执行 ls,那么ls返回的结果是 ~ 这个目录的,而不是~\web 。那么如何让cd这个命令起效果呢?用“; ” ── cd ~\web; ls
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端