Ruby, Run Linux befiehlt nacheinander von SSH und protokollieren Sie alles
-
25-10-2019 - |
Frage
Ich möchte Code in Ruby Witch Net :: SSH schreiben, in dem Befehle einzeln auf dem Remote -Linux -Computer ausführen und alles protokollieren (sogenannte Befehl, StDout und Stderr auf Linux -Maschine).
Also schreibe ich Funktion:
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
Zum Beispiel, wenn ich auf Remote -Linux -neuen Ordnern und Datei erstellen möchte: "./verlongdirname/anotherlongdirname/a.txt" und listet Dateien in diesem Direcotry auf und finde Firefox dort (was ein wenig dumm ist: P), also rufe ich an Oben wie diese:
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
Nach dem obigen Auslaufcode habe ich vollständige Protokoll -Witch -Informationen zu Ausführungsbefehlen in Zeile 1,#2,#3,#4,#5. Das Problem ist, dass der Status unter Linux, zwischen Managerbefehlen aus dem CMDS -Array, nicht gespeichert wird (daher muss ich "CD" -Anweisung wiederholen, bevor ich den richtigen Befehl ausführen kann). Und damit bin ich nicht zufrieden.
Mein Ziel ist es, solche CMDS -Tabellen zu haben:
cmds=["mkdir verylongdirname", \ #1
"cd verylongdirname", \
"mkdir anotherlongdirname", \ #2
"cd anotherlongdirname", \
"touch a.txt", \ #3
"ls -la", \ #4
"find ./ firefox"] #5
Wie Sie sehen, wird der Status zwischen dem Ausführen jedes Befehls auf der Linux -Maschine gespeichert (und wir benötigen keine wiederholte "CD" -Anweisung, bevor wir den richtigen Befehl ausführen). Wie ändere ich "RS (SSH, CMDS)" -Ver Prozedur, um es zu tun und alles zu protokollieren (Comand, Stdout, Stdin) wie zuvor?
Lösung
Versuchen Sie es vielleicht mit einem SSH -Kanal, um stattdessen eine Remote -Shell zu öffnen. Dies sollte den Zustand zwischen Ihren Befehlen bewahren, da die Verbindung offen gehalten wird:
http://net-ssh.github.com/sssh/v1/chapter-5.html
Hier ist auch ein Artikel, in dem Sie etwas Ähnliches mit einem etwas anderen Ansatz tun:
http://drnicwilliams.com/2006/09/22/remote-shell-with-ruby/
Bearbeiten 1:
OK. Ich sehe, was du sagst. SyncShell
wurde aus dem Netz entfernt :: SSH 2.0. Ich fand das jedoch, was so aussieht, als würde es so ziemlich das tun, was SyncShell
tat:
http://net-ssh-telnet.rubyforge.org/
Beispiel:
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
Dh Net::SSH::Telnet
ist synchron und bewahrt den Zustand, weil es in einer PTY mit Ihrer Fernschalenumgebung läuft. Denken Sie daran, die richtige Eingabeaufforderung zu erkennen, ansonsten Net::SSH::Telnet
Wird anscheinend hängen, sobald Sie es anrufen (es versucht, die Eingabeaufforderung zu finden).
Andere Tipps
Sie können stattdessen Rohr verwenden:
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, schließlich bekomme ich mit Hilfe von @Casper das Verfahren (maby, jemand benutzt es):
# 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
Und wir nennen es im Code:
#!/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
Danke Tschüss.
Hier ist Wrapper um Net/SSH hier ist Artikel http://ruby-lang.info/blog/virtual-file-system-b3g
Quelle https://github.com/alexeypether/vfs
Um alle Befehle zu protokollieren, überschreiben Sie die Box.Bash -Methode und fügen Sie dort die Protokollierung hinzu