Ruby, запускай команды linux одну за другой, по SSH и LOG все - PullRequest
7 голосов
/ 24 июля 2011

Я хочу написать код на Ruby witch net :: ssh, который будет запускать команды одну за другой на удаленной машине Linux и регистрировать все (называемые командами, 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», перечислить файлы в этом каталоге и найти там 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, между исполняемыми командами из массива cmds, не сохраняется (поэтому я должен повторить оператор «cd» перед выполнением правильной команды). И меня это не устраивает.

Моя цель - создать такие таблицы cmds:

  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), как раньше?

Ответы [ 4 ]

3 голосов
/ 24 июля 2011

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

http://net -ssh.github.com / SSH / v1 / капитула 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 /

* +1025 * Пример:
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 будет зависать при вызове (он пытается найти подсказку).

2 голосов
/ 26 июля 2011

Хорошо, наконец, с помощью @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

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

2 голосов
/ 25 июля 2011

Вы можете использовать трубу вместо:

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"]
0 голосов
/ 06 августа 2011

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

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

чтобы записать все команды, просто перезапишите метод Box.bash и добавьте туда логирование

...