2011-07-24 31 views
6

Tôi muốn viết mã trong Ruby witch net :: ssh chạy từng lệnh một trên máy linux từ xa và đăng nhập mọi thứ (gọi là lệnh, stdout và stderr on máy linux).Ruby, chạy lệnh linux từng cái một, bằng SSH và LOG tất cả mọi thứ

Vì vậy, tôi viết hàm:

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 

Ví dụ nếu tôi muốn tạo trên Linux từ xa các thư mục mới và tập tin: "./verylongdirname/anotherlongdirname/a.txt", và các tập tin danh sách trong direcotry đó, và tìm firefox ở đó (đó là ngu ngốc một chút: P) vì vậy tôi gọi trên thủ tục như thế:

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 

Sau khi mã chạy trên tôi sẽ có thông tin phù thủy đầy đủ LOG về hành lệnh trong dòng # 1, # 2, # 3, # 4, # 5. Vấn đề là trạng thái trên linux, giữa các lệnh execude từ mảng cmds, không được lưu (vì vậy tôi phải lặp lại câu lệnh "cd" trước khi chạy lệnh thích hợp). Và tôi không thỏa mãn điều đó.

Mục đích của tôi là phải có bảng cmds như thế:

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

Như bạn thấy, te trạng thái giữa chạy mỗi lệnh là lưu trên máy Linux (và chúng ta không cần lặp lại apropriate "cd" tuyên bố trước khi chạy lệnh thích hợp). Làm thế nào để thay đổi "rs (ssh, cmds)" thủ tục để làm điều đó và LOG tất cả mọi thứ (comand, stdout, stdin) như trước?

Trả lời

3

Có thể dùng thử kênh ssh thay vì mở vỏ từ xa. Điều đó sẽ duy trì trạng thái giữa các lệnh của bạn khi kết nối sẽ được giữ mở:

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

Đây là cũng là một bài viết làm một cái gì đó tương tự với một cách tiếp cận khác nhau chút:

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

Sửa 1:

Ok. Tôi thấy những gì bạn đang nói. SyncShell đã bị xóa khỏi Net :: SSH 2.0. Tuy nhiên tôi thấy điều này, mà hình như nó làm khá nhiều những gì SyncShell đã làm:

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

Ví dụ:

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 

Tức là Net::SSH::Telnet là đồng bộ, và bảo toàn trạng thái, vì nó chạy trong một pty với môi trường vỏ từ xa của bạn. Hãy nhớ đặt phát hiện lời nhắc chính xác, nếu không Net::SSH::Telnet sẽ xuất hiện để treo khi bạn gọi nó (nó đang cố gắng tìm lời nhắc).

+0

1. Trong http: // drnicwilliams. com/2006/09/22/remote-shell-with-ruby/chúng tôi thấy: 'shell = session.shell.sync' Web thứ hai là từ năm 2006. Hôm nay trong ruby ​​shell.sync không hoạt động. Shell có thể bị rơi khỏi net phát triển :: ssh (Tôi không biết tại sao). Có một số gói phần mềm thay thế net-ssh-shell nhưng là để trẻ sử dụng nó (không có nhiều chức năng được thực hiện ở đó). –

+0

2. Tôi đã đọc http://net-ssh.github.com/ssh/v1/chapter-5.html trước đây. Khi tôi cố gắng sử dụng các kênh với on_data của nó (cho stdout), on_extended_data (cho stderr) và send_data (để gửi lệnh một by one in loop) chức năng - nhưng tôi không thể lực lượng đăng nhập tuần tự (để đăng nhập tuần tự command1, stdout1, stderr1 , command2, stdout2, stderr2, command3, stdout3, sterr3 ...). Lý do là trong send_data - tất cả các lệnh được gửi trước khi nó được thực hiện trên máy từ xa (vì vậy trong log chúng ta thấy: command1, command2, command3, ..., stdout1, stderr1, stdout2, stderr2, stdout3, stderr3 .. .) –

+1

@ user860099 - Xem chỉnh sửa của tôi. – Casper

2

Bạn có thể sử dụng ống thay vì:

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

Giải pháp đó không hoạt động trong Windows - vì vậy nó không có tính di động. Tôi không kiểm tra nó trong linux, nhưng cảm ơn thời gian của bạn. Maby trong tương lai ai đó sử dụng nó. –

+0

Kiểm tra liên kết này http://rubyforge.org/projects/win32utils ('win32-popen3') –

2

Ok, cuối cùng với sự giúp đỡ của @Casper tôi nhận được các thủ tục (maby một ai đó sử dụng nó):

# 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 

Và chúng tôi gọi nó trong mã :

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

Xin cảm ơn, tạm biệt.

Các vấn đề liên quan