Ruby, run linux commands one by one, SSH and LOG all

I want to write code in Ruby witch net :: ssh that runs commands one after another on a remote Linux computer and writes everything (called a command, stdout and stderr on a linux machine).

So, I am writing a function:

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 

For example, if I want to create new folders and a file on remote linux: "./verylongdirname/anotherlongdirname/a.txt" and list the files in this directory and find firefox there (which is a bit stupid: P), so I call the similar procedure above:

 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 

After executing the above code, I will have full information about the command files about the execution commands in lines # 1, # 2, # 3, # 4, # 5. The problem is that the state in linux between execude commands from the cmds array is not saved (therefore I have to repeat the "cd" instruction before running the appropriate command). And I am not satisfied with this.

My goal is to have these cmds tables:

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

As you can see, the te state between the start of each command is saved on the linux machine (and we do not need to repeat the appropriate "cd" instruction before running the corresponding command). How to change the procedure "rs (ssh, cmds)" to do this, and LOG ALLVER (comand, stdout, stdin), as before?

+6
source share
4 answers

Perhaps try using the ssh channel to open a remote shell. This should keep state between your commands, as the connection will remain open:

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

Here's also an article about something similar with a slightly different approach:

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

Change 1 :

Ok I see what you are saying. SyncShell been removed from Net :: SSH 2.0. However, I found this, which is similar to what SyncShell did:

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

Example:

 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 

those. Net::SSH::Telnet is synchronous and stateful since it works in pty with a remote shell environment. Do not forget to set the correct prompt detection, otherwise Net::SSH::Telnet will freeze as soon as you call it (it tries to find the prompt).

+3
source

Instead, you can use 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"] 
+2
source

Ok, finally with @Casper I get the procedure (someone uses it):

  # 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 

And we call it 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 

Thanks, bye.

+2
source

Here's a wrapper around Net / ssh here. http://ruby-lang.info/blog/virtual-file-system-b3g

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

to record all commands, just overwrite the Box.bash method and add the log there

0
source

Source: https://habr.com/ru/post/893442/


All Articles