Ruby, запускает команды linux один за другим, по SSH и регистрирует все

Я хочу написать код в Ruby witch net:: ssh, который запускает команды один за другим на удаленной машине linux и регистрирует все (называемые command, stdout и stderr на машине linux).

поэтому я пишу функцию:

  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", и список файлов в этом direcotry, и найти 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. Проблема в том, что состояние в linux между командами execude из массива cmds не сохраняется (поэтому я должен повторить инструкцию "cd" перед запуском правильной команды). И я не удовлетворен этим.

моя цель-иметь такие таблицы cmds:

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

как вы видите, состояние te между запуском каждой команды сохраняется на машине linux (и мы не нужно повторять инструкцию "cd" перед запуском правильной команды). Как изменить процедуру" rs(ssh,cmds)", чтобы сделать это и зарегистрировать все (comand,stdout, stdin), как раньше?

4 ответов


возможно, попробуйте использовать ssh-канал, чтобы открыть удаленную оболочку. Это должно сохранить состояние между вашими командами, поскольку соединение будет оставаться открытым:

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

вот также статья о том, как сделать что-то подобное с немного другим подходом:

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

изменить 1:

Ok. Я понимаю, о чем вы говорите. 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 появится вешать как только вы его вызовете (он пытается найти подсказку).


вместо этого вы можете использовать pipe:

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"]

Ok, наконец, с помощью @Casper я получаю процедуру (maby кто-то ее использует):

  # 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] "+; 
        end
        d=        
      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

Спасибо, пока.


вот обертка вокруг Net / ssh вот статья http://ruby-lang.info/blog/virtual-file-system-b3g

источник https://github.com/alexeypetrushin/vfs

для регистрации всех команд просто перезаписать поле.bash метод и добавить ведение журнала там