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 метод и добавить ведение журнала там