Ruby,通过 SSH 和 LOG 逐一运行 linux 命令

发布于 2024-11-26 03:26:13 字数 1608 浏览 3 评论 0原文

我想用 Ruby 女巫 net::ssh 编写代码,在远程 Linux 机器上一一运行命令并记录所有内容(在 Linux 机器上称为命令、stdout 和 stderr)。

所以我编写函数:

  def rs(ssh,cmds)
    cmds.each do |cmd|
      log.debug "[SSH>] #{cmd}"
      ssh.exec!(cmd) do |ch, stream, data|    
        log.debug "[SSH:#{stream}>] #{data}"            
      end  
    end
  end

例如,如果我想在远程linux上创建新文件夹和文件:“./verylongdirname/anotherlongdirname/a.txt”,并列出该目录中的文件,并在那里找到firefox(这有点愚蠢: P)所以我这样调用上面的过程:

Net::SSH.start(host, user, :password => pass) do |ssh|  

  cmds=["mkdir verylongdirname", \                                 #1
        "cd verylongdirname; mkdir anotherlongdirname, \           #2
        "cd verylongdirname/anotherlongdirname; touch a.txt", \    #3
        "cd verylongdirname/anotherlongdirname; ls -la", \         #4
        "cd verylongdirname/anotherlongdirname; find ./ firefox"   #5 that command send error to stderr.
        ]

  rs(ssh,cmds)   # HERE we call our function

  ssh.loop
end

运行上面的代码后,我将在#1、#2、#3、#4、#5 行中获得有关执行命令的完整 LOG 信息。问题是Linux上的状态,在cmds数组中执行命令之间,没有被保存(所以我必须在运行正确的命令之前重复“cd”语句)。我对此并不满意。

我的目的是拥有这样的 cmd 表:

  cmds=["mkdir verylongdirname", \     #1
        "cd verylongdirname", \        
        "mkdir anotherlongdirname", \  #2
        "cd anotherlongdirname", \
        "touch a.txt", \               #3
        "ls -la", \                    #4
        "find ./ firefox"]             #5

如您所见,运行每个命令之间的状态保存在 Linux 计算机上(并且在运行正确的命令之前我们不需要重复适当的“cd”语句)。如何更改“rs(ssh,cmds)”程序来执行此操作并像以前一样记录所有内容(comand、stdout、stdin)?

I want to write code in Ruby witch net::ssh that run commands one by one on remote linux machine and log everything (called command, stdout and stderr on linux machine).

So I write function:

  def rs(ssh,cmds)
    cmds.each do |cmd|
      log.debug "[SSH>] #{cmd}"
      ssh.exec!(cmd) do |ch, stream, data|    
        log.debug "[SSH:#{stream}>] #{data}"            
      end  
    end
  end

For example if I want to create on remote linux new folders and file: "./verylongdirname/anotherlongdirname/a.txt", and list files in that direcotry, and find firefox there (which is stupid a little :P) so i call above procedure like that:

Net::SSH.start(host, user, :password => pass) do |ssh|  

  cmds=["mkdir verylongdirname", \                                 #1
        "cd verylongdirname; mkdir anotherlongdirname, \           #2
        "cd verylongdirname/anotherlongdirname; touch a.txt", \    #3
        "cd verylongdirname/anotherlongdirname; ls -la", \         #4
        "cd verylongdirname/anotherlongdirname; find ./ firefox"   #5 that command send error to stderr.
        ]

  rs(ssh,cmds)   # HERE we call our function

  ssh.loop
end

After run code above i will have full LOG witch informations about executions commands in line #1,#2,#3,#4,#5. The problem is that state on linux, between execude commands from cmds array, is not saved (so I must repeat "cd" statement before run proper command). And I'm not satisfy with that.

My purpose is to have cmds tables like that:

  cmds=["mkdir verylongdirname", \     #1
        "cd verylongdirname", \        
        "mkdir anotherlongdirname", \  #2
        "cd anotherlongdirname", \
        "touch a.txt", \               #3
        "ls -la", \                    #4
        "find ./ firefox"]             #5

As you see, te state between run each command is save on the linux machine (and we don't need repeat apropriate "cd" statement before run proper command). How to change "rs(ssh,cmds)" procedure to do it and LOG EVERYTHING (comand,stdout,stdin) like before?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

乖乖兔^ω^ 2024-12-03 03:26:13

也许尝试使用 ssh 通道来打开远程 shell。这应该保留命令之间的状态,因为连接将保持打开状态:

http: //net-ssh.github.com/ssh/v1/chapter-5.html

这里还有一篇文章用稍微不同的方法做类似的事情:

http://drnicwilliams.com/2006/09/22/remote-shell- with-ruby/

编辑 1

好的。我明白你在说什么。 SyncShell 已从 Net::SSH 2.0 中删除。然而我发现这个,看起来它的作用与 SyncShell 的作用差不多:

http ://net-ssh-telnet.rubyforge.org/

示例:

s = Net::SSH.start(host, user)
t = Net::SSH::Telnet.new("Session" => s, "Prompt" => %r{^myprompt :})
puts t.cmd("cd /tmp")  
puts t.cmd("ls")       # <- Lists contents of /tmp

Net::SSH::Telnet 是同步的,并保留状态,因为它与远程设备一起在 pty 中运行外壳环境。请记住设置正确的提示检测,否则一旦您调用Net::SSH::Telnet,它就会显示为挂起(它正在尝试查找提示)。

Perhaps try it with an ssh channel instead to open a remote shell. That should preserve state between your commands as the connection will be kept open:

http://net-ssh.github.com/ssh/v1/chapter-5.html

Here's also an article of doing something similar with a little bit different approach:

http://drnicwilliams.com/2006/09/22/remote-shell-with-ruby/

Edit 1:

Ok. I see what you are saying. SyncShell was removed from Net::SSH 2.0. However I found this, which looks like it does pretty much what SyncShell did:

http://net-ssh-telnet.rubyforge.org/

Example:

s = Net::SSH.start(host, user)
t = Net::SSH::Telnet.new("Session" => s, "Prompt" => %r{^myprompt :})
puts t.cmd("cd /tmp")  
puts t.cmd("ls")       # <- Lists contents of /tmp

I.e. Net::SSH::Telnet is synchronous, and preserves state, because it runs in a pty with your remote shell environment. Remember to set the correct prompt detection, otherwise Net::SSH::Telnet will appear to hang once you call it (it's trying to find the prompt).

七婞 2024-12-03 03:26:13

您可以使用管道代替:

require "open3"

SERVER = "..."
BASH_PATH = "/bin/bash"

BASH_REMOTE = lambda do |command|
  Open3.popen3("ssh #{SERVER} #{BASH_PATH}") do |stdin, stdout, stderr|
    stdin.puts command
    stdin.close_write
    puts "STDOUT:", stdout.read
    puts "STDERR:", stderr.read
  end
end

BASH_REMOTE["ls /"]
BASH_REMOTE["ls /no_such_file"]

You can use pipe instead:

require "open3"

SERVER = "..."
BASH_PATH = "/bin/bash"

BASH_REMOTE = lambda do |command|
  Open3.popen3("ssh #{SERVER} #{BASH_PATH}") do |stdin, stdout, stderr|
    stdin.puts command
    stdin.close_write
    puts "STDOUT:", stdout.read
    puts "STDERR:", stderr.read
  end
end

BASH_REMOTE["ls /"]
BASH_REMOTE["ls /no_such_file"]
夜灵血窟げ 2024-12-03 03:26:13

好吧,最后在 @Casper 的帮助下我得到了程序(也许有人使用它):

  # Remote command execution
  # t=net::ssh:telnet, c="command_string"
  def cmd(t,c)    
    first=true
    d=''    
    # We send command via SSH and read output piece by piece (in 'cm' variable)
    t.cmd(c) do |cm|       
      # below we cleaning up output piece (becouse it have strange chars)     
      d << cm.gsub(/\e\].*?\a/,"").gsub(/\e\[.*?m/,"").gsub(/\r/,"")     
      # when we read entire line(composed of many pieces) we write it to log
      if d =~ /(^.*?)\n(.*)$/m
        if first ; 
          # instead of the first line (which has repeated commands) we log commands 'c'
          @log.info "[SSH]>"+c; 
          first=false
        else
          @log.info "[SSH] "+$1; 
        end
        d=$2        
      end      
    end

    # We print lines that were at the end (in last piece)
    d.each_line do |l|
      @log.info "[SSH] "+l.chomp      
    end
  end

我们用代码调用它:

#!/usr/bin/env ruby

require 'rubygems'

require 'net/ssh'

require 'net/ssh/telnet'

require 'log4r'
...
...
...
Net::SSH.start(host, user, :password => pass) do |ssh|  
  t = Net::SSH::Telnet.new("Session" => ssh)
  cmd(t,"cd /")
  cmd(t,"ls -la")
  cmd(t,"find ./ firefox")  
end

谢谢,再见。

Ok, finally with the help of @Casper i get the procedure (maby someone use it):

  # Remote command execution
  # t=net::ssh:telnet, c="command_string"
  def cmd(t,c)    
    first=true
    d=''    
    # We send command via SSH and read output piece by piece (in 'cm' variable)
    t.cmd(c) do |cm|       
      # below we cleaning up output piece (becouse it have strange chars)     
      d << cm.gsub(/\e\].*?\a/,"").gsub(/\e\[.*?m/,"").gsub(/\r/,"")     
      # when we read entire line(composed of many pieces) we write it to log
      if d =~ /(^.*?)\n(.*)$/m
        if first ; 
          # instead of the first line (which has repeated commands) we log commands 'c'
          @log.info "[SSH]>"+c; 
          first=false
        else
          @log.info "[SSH] "+$1; 
        end
        d=$2        
      end      
    end

    # We print lines that were at the end (in last piece)
    d.each_line do |l|
      @log.info "[SSH] "+l.chomp      
    end
  end

And we call it in code:

#!/usr/bin/env ruby

require 'rubygems'

require 'net/ssh'

require 'net/ssh/telnet'

require 'log4r'
...
...
...
Net::SSH.start(host, user, :password => pass) do |ssh|  
  t = Net::SSH::Telnet.new("Session" => ssh)
  cmd(t,"cd /")
  cmd(t,"ls -la")
  cmd(t,"find ./ firefox")  
end

Thanks, bye.

快乐很简单 2024-12-03 03:26:13

这是 Net/ssh 的包装,这是文章 http://ruby-lang.info/blog /virtual-file-system-b3g

https://github.com/alexeypetrushin/vfs

记录所有命令只需覆盖 Box.bash 方法并在那里添加日志记录

Here's wrapper around Net/ssh here's article http://ruby-lang.info/blog/virtual-file-system-b3g

source https://github.com/alexeypetrushin/vfs

to log all commands just overwrite the Box.bash method and add logging there

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文