Using Net::SSH to login to shell and get stateful output - ruby

I was reading the documentation (which is pretty outdated). Net-ssh does not have .shell method anymore? how can i achieve signing into the shell, run command and get stdout until its done?
Can anyone point me to a good documentation or advice on how i can do it?
Thank you

Net::SSH has been updated a few days ago, and using the first example of the README on the first page of the project did exactly what you wanted to do :
require 'net/ssh'
Net::SSH.start('my_server', 'my_user') do |ssh|
output = ssh.exec!("ls")
puts output
end
#=>
# 20130402_083136_DSCF0923.jpg
# 20160715_113357_DSC_6050.jpg
# 20160715_121646_DSC_2.jpg
...
...

Related

Can I programmatically interact with an "installer" (ie. ./install.sh) [duplicate]

Is there an Expect equivalent gem for Ruby?
I tried searching on code.google and rubygems.org, but sadly it did not show up.
FYI: Expect is a Unix automation and testing tool, written by Don Libes as an extension to the Tcl scripting language, for interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, ssh, and others.
Ruby comes with the PTY module for setting up pseudoterminals to drive interactive command line applications. With it comes an expect method that allows you to interact with an application kinda like Expect. For learning how to use expect, I found "What to expect from the Ruby expect library?" helpful.
As far as gems go, maybe checkout greenletters which is supposed to improve upon PTY + expect (although I haven't tried it myself).
I recently spent quite a bit of time struggling with this issue (I am stuck with 1.8.7). I found this question, this blog post and this forum thread really useful.
At the end this is my application code if anyone is interested in a little example (pass the password to rpm when signing packages):
def run_interactive command, password, promt
output = ''
begin
r, w, pid = PTY.spawn(command)
puts r.expect(promt)
sleep(0.5)
w.puts(password)
begin
r.each { |l| output += l }
rescue Errno::EIO
end
$?.exitstatus
Process.wait(pid)
rescue PTY::ChildExited => e
$stderr.puts "The child process #{e} exited! #{$!.status.exitstatus}"
end
output
end
password = "mypassword"
command = "rpm --define '_signature gpg' --define '_gpg_name #{key_id}' --addsign #{package}"
promt = %r{.*: }
expected = %r{good}
output = run_interactive(command, password, promt)
if output.match(expected)
puts output
else
abort "Error: expected: '#{expected}' got '#{output}'"
end
It has little error checking but it was all I needed.
Edit: Update the code with Process.wait(pid) to make sure it finishes before continuing and add comment about this being for 1.8.7.
checkout this rubygem: https://github.com/abates/ruby_expect. It could handle some small task for you. from its official example, it's enough to 'enter password' and login and interactive with local script.
here is an example that update the git code (which is authenticated with password):
require 'rubygems'
require 'ruby_expect'
def update_code
password = 'your password here'
exp = RubyExpect::Expect.spawn('git pull', :debug => true)
exp.procedure do
each do
expect /password: / do
send password
end
end
end
end
update_code
just run the code above, and your will see like this:
$ ruby update_code.rb
shensiwei#gforge.1ver??.net's password:
remote: Counting objects: 133, done.
remote: Compressing objects: 100% (84/84), done.
remote: Total 85 (delta 62), reused 0 (delta 0)
Unpacking objects: 100% (85/85), done.
for more example and details, please dive into its source code.
expect4r seems to do what you are asking for, though it is made specific for connections to Cisco and Juniper devices.
Perhaps even better is yax as this is "yet another expect".
RExpect
From the project's website:
RExpect is a drop in replacement for the expect.rb module in the
standard library that is faster and more robust, cabable of driving
many devices simultaneously.
parley is another one you can try, (written by me). It is inspired by Perl expect.

Ruby - Running rb file from script [duplicate]

This question already has answers here:
Running another ruby script from a ruby script
(7 answers)
Closed 7 years ago.
I'd like to write a ruby script that then calls another ruby script.
Example, I'd like to run the "test1.rb" from my script.
The test1.rb has been simplified to just do this:
print "1"
Then get the result (-> 1).
I tried to complete this problem with backticks or another executing command (%x[#{"test1.rb"}], system("test1.rb") etc.), but it didn't work.
So any idea how I call one script that then calls another script (either relinquishing total control or forking), and get the results?
Thanks
You can simply require the file, which would load the code and execute it:
require_relative "path/test1"
For the sake of having controll over the the run code, I would advice to place your script in a method:
# In test1.rb
def exec_my_script
puts 1
end
# In your main script
require_relative "path/test1"
exec_my_script
EDIT: Ok, since this does not seem to work for your usecase, you can read the file as string and eval the string as so:
result = eval(File.read("path/test1.rb"))
# do something with result
I do NOT like this approach, because it feels kinda "hacky" and is by all means insecure and it will only work if the last thing called in your test1 script returns the result you need...
You may want to use open3
require 'open3'
cmd = 'ruby test1.rb'
#You may change the contents of cmd like you would run it from the command line; like ruby [directory]/filename
Open3.popen3(cmd) do |stdin, stdout|
var = stdout.read
puts var
end
system('ruby test1.rb') # should do the trick
You can also make test1 executable (with chmod) and add the shebang line on top of test1.
You then can call
system("./test1.rb")
Ok, the system('ruby test1.rb') and system("ruby", "test1.rb") command worked.
And now, I like to set the returning value ("1") to a variable.
How I do it? It's possible to do that with backticks?

A ruby script to run tail on a log file?

I want to write a ruby script that read from a config file that will have filenames, and then when I run the script it will take the tail of each file and output the console.
What's the best way to go about doing this?
Take a look at File::Tail gem.
You can invoke linux tail -number_of_lines file_name command from your ruby script and let it print on console or capture output and print it yourself (if you need to do something with these lines before you print it)
We have a configuration file that contain a list of the log files; for example, like this:
---
- C:\fe\logs\front_end.log
- C:\mt\logs\middle_tier.log
- C:\be\logs\back_end.log
The format of the configuration file is a yaml simple sequence , therefore suppose we named this file 'settings.yaml'
The ruby script that take the tail of each file and output the console could be like this:
require 'yaml'
require 'file-tail'
logs = YAML::load(File.open('settings.yaml'))
threads = []
logs.each do |the_log|
threads << Thread.new(the_log) { |log_filename|
File.open(log_filename) do |log|
log.extend(File::Tail)
log.interval = 10
log.backward(10)
log.tail { |line| p "#{File.basename(the_log,".log")} - #{line}" }
end
}
end
threads.each { |the_thread| the_thread.join }
Note: displaying each line I wanted to prefix it with the name of the file from which it originates, ...this for me is a good option but you can edit the script to change as you like ; is the same for the tails parameters.
if file-tail is missing in your environment, follow the link as #Mark Thomas posts in his answear; i.e you need to:
> gem install file-tail
I found the file-tail gem to be a bit buggy. I would write to a file and it would read the entire file again instead of just thelines appended. This happened even though I had log.backward set to 0. I ended up writing my own and figured that I would share it here in case any one else is looking for a Ruby alternative to the file-tail gem. You can find the repo here. It uses non_blocking io, so it will catch amendments to the file immediately. There is one caveat that can be easily fixed if you can program in the Ruby programming language; log.backward is hard coded to be -1.

Mercurial HG_NODE hook variable on windows

I'm currently testing mercurial hooks on windows and it seems like I cannot access hook variables....
here's hgrc content :
[hooks] prechangegroup = ruby prechangegroup.rb test1 test2 $HG_NODE
I also tried with %HG_NODE%
Here's prechangegroup.rb content
ARGV.each do|a|
puts "Argument: #{a}"
end
It prints out:
Argument: test1
Argument: test2
Argument: $HG_NODE$
Followed by the normal push output...
Any idea? (probably something stupid but, I can't seem to find it)
Thanks
HG_NODE is an environmental variable. You don't have to use it as arguments on the command line. Instead, you should be able to use it as puts ENV['HG_NODE'] (found through search engine as I'm not a ruby guy)
OK, I found a good documentation right on mercurial's website.
http://www.selenic.com/mercurial/hgrc.5.html#hooks
I tried with a variable other than %HG_NODE% like %HG_URL% and the variable worked.
So it probably means that the variable is inaccessible from that hook.

Equivalent of cURL for Ruby?

Is there a cURL library for Ruby?
Curb and Curl::Multi provide cURL bindings for Ruby.
If you like it less low-level, there is also Typhoeus, which is built on top of Curl::Multi.
Use OpenURI and
open("http://...", :http_basic_authentication=>[user, password])
accessing sites/pages/resources that require HTTP authentication.
Curb-fu is a wrapper around Curb which in turn uses libcurl. What does Curb-fu offer over Curb? Just a lot of syntactic sugar - but that can be often what you need.
HTTP clients is a good page to help you make decisions about the various clients.
You might also have a look at Rest-Client
If you know how to write your request as a curl command, there is an online tool that can turn it into ruby (2.0+) code: curl-to-ruby
Currently, it knows the following options: -d/--data, -H/--header, -I/--head, -u/--user, --url, and -X/--request. It is open to contributions.
the eat gem is a "replacement" for OpenURI, so you need to install the gem eat in the first place
$ gem install eat
Now you can use it
require 'eat'
eat('http://yahoo.com') #=> String
eat('/home/seamus/foo.txt') #=> String
eat('file:///home/seamus/foo.txt') #=> String
It uses HTTPClient under the hood. It also has some options:
eat('http://yahoo.com', :timeout => 10) # timeout after 10 seconds
eat('http://yahoo.com', :limit => 1024) # only read the first 1024 chars
eat('https://yahoo.com', :openssl_verify_mode => 'none') # don't bother verifying SSL certificate
Here's a little program I wrote to get some files with.
base = "http://media.pragprog.com/titles/ruby3/code/samples/tutthreads_"
for i in 1..50
url = "#{ base }#{ i }.rb"
file = "tutthreads_#{i}.rb"
File.open(file, 'w') do |f|
system "curl -o #{f.path} #{url}"
end
end
I know it could be a little more eloquent but it serves it purpose. Check it out. I just cobbled it together today because I got tired of going to each URL to get the code for the book that was not included in the source download.
There's also Mechanize, which is a very high-level web scraping client that uses Nokogiri for HTML parsing.
Adding a more recent answer, HTTPClient is another Ruby library that uses libcurl, supports parallel threads and lots of the curl goodies. I use HTTPClient and Typhoeus for any non-trivial apps.
To state the maybe-too-obvious, tick marks execute shell code in Ruby as well. Provided your Ruby code is running in a shell that has curl:
puts `curl http://www.google.com?q=hello`
or
result = `
curl -X POST https://www.myurl.com/users \
-d "name=pat" \
-d "age=21"
`
puts result
A nice minimal reproducible example to copy/paste into your rails console:
require 'open-uri'
require 'nokogiri'
url = "https://www.example.com"
html_file = URI.open(url)
doc = Nokogiri::HTML(html_file)
doc.css("h1").text
# => "Example Domain"

Resources