2011-07-24 20 views
6

Chcę napisać kod w sieci Ruby: net ssh, który uruchamia komendy jeden po drugim na zdalnym komputerze linuxowym i rejestruje wszystko (polecenia, stdout i stderr na maszyna linuxowa).Ruby, uruchamiaj komendy linuksowe jeden po drugim, SSH i LOG wszystko, co chcesz

Więc funkcję napisać:

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 

Na przykład, jeśli chcesz utworzyć na zdalny linux nowych folderów i plików: „./verylongdirname/anotherlongdirname/a.txt” oraz lista plików w tym direcotry, i znaleźć firefox nie (co jest głupie trochę: P), więc zgłoszę powyżej procedury tak:

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 

Po kodzie wykonywania wyżej będę miał pełny raport informacje czarownica o egzekucjach komendy w linii # 1, # 2, # 3, # 4, # 5. Problem polega na tym, że stan w systemie Linux, pomiędzy poleceniami execude z tablicy cmds, nie jest zapisywany (więc muszę powtórzyć instrukcję "cd" przed uruchomieniem odpowiedniego polecenia). I nie jestem z tego zadowolony.

Moim celem jest mieć CMDS tabele tak:

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

Jak widać, te państwa między metę każda komenda jest zaoszczędzić na maszynie Linux (i nie musimy powtarzać odniesieniu do odpowiednich „CD” oświadczenie przed uruchomieniem odpowiedniego polecenia). Jak zmienić "rs (ssh, cmds)" aby to zrobić i ZALOGUJ SIĘ WSZYSTKO (comand, stdout, stdin) jak wcześniej?

Odpowiedz

3

Być może spróbuj użyć kanału SSH, aby otworzyć zdalną powłokę. To powinno zachować stan pomiędzy swoimi poleceń jako połączenie zostanie utrzymane otwarte:

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

Tutaj również artykuł robi coś podobnego z trochę innego podejścia:

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

Edytuj 1:

Ok. Widzę, co mówisz. SyncShell został usunięty z Net :: SSH 2.0. Jednak uważam, że ten, który wygląda jak to robi dość dużo, co SyncShell zrobił:

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

Przykład:

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 

Tj Net::SSH::Telnet jest synchroniczna i zachowuje stan, ponieważ działa w środowisku PTY ze środowiskiem zdalnej powłoki. Pamiętaj, aby ustawić poprawne wykrywanie monitów, w przeciwnym razie Net::SSH::Telnet pojawi się, aby zawiesić się po wywołaniu (próbuje znaleźć monit).

+0

1. http: // drnicwilliams. com/2006/09/22/remote-shell-with-ruby/widzimy: 'shell = session.shell.sync' Thad web jest z 2006 roku. Dzisiaj w ruby ​​shell.sync nie działa. Shell prawdopodobnie został usunięty z develop net :: ssh (nie wiem dlaczego). Jest trochę alternatywnego pakietu net-ssh-shell, ale jest młody, aby go użyć (nie ma tam wiele funkcji). –

+0

2. Czytam wcześniej http://net-ssh.github.com/ssh/v1/chapter-5.html. Kiedy próbuję użyć kanałów z jego danymi wyjściowymi (dla stdout), on_extended_data (dla stderr) i send_data (dla poleceń wysyłania jeden po drugim w pętli) funkcje - ale nie mogę wymusić dziennika sekwencyjnego (aby logować sekwencyjne polecenie1, stdout1, stderr1 , command2, stdout2, stderr2, command3, stdout3, sterr3 ...). Powodem jest to, że w send_data - wszystkie polecenia były wysyłane zanim pierwsze z nich zostaną wykonane na zdalnym komputerze (tak w logu widzimy: command1, command2, command3, ..., stdout1, stderr1, stdout2, stderr2, stdout3, stderr3 .. .) –

+1

@ user860099 - Zobacz moją edycję. – Casper

2

Można użyć rury Zamiast:

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

To rozwiązanie nie działa w systemie Windows - więc nie jest przenośne. Nie sprawdzam tego w systemie Linux, ale dziękuję za poświęcony czas. Maby w przyszłości ktoś z niego skorzysta. –

+0

Sprawdź ten link http://rubyforge.org/projects/win32utils ('win32-popen3') –

2

Ok, wreszcie z pomocą @Casper dostaję postępowania (maby ktoś go używać):

# 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 

A nazywamy go w kodzie :

#!/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 

Dzięki, pa.