How to find files in ruby which were edited in some time interval? - ruby

How to find files in a directory which were edited in last n minutes?
In unix which is -mmin -60.
In host
ruby /home/ava/test works fine!
Net::SSH.start('host', 'ava') do |ssh|
`ruby /home/ava/test`
end
gives ruby: No such file or directory -- /home/ava/test (LoadError)

You could get a list of files using Dir.[], and use File.mtime on each one to filter them:
Dir["*"].select { |fname| File.mtime(fname) > (Time.now - 60) }

The problem with your code is that Ruby isn't control on the remote system you connect to, instead, the shell on that system is, and you're merely able to issue commands, as if you'd ssh'd into your own local system. Ruby's built-in commands, like Dir.chdir only apply locally, not to the remote session.
Your best bet is to write a script you execute that resides on that system, otherwise the task of executing commands becomes more difficult and you'll need to anticipate prompts and possibly various responses from commands on that system as your code executes things.
The Net::SSH gem includes examples showing how to issue remote commands; You need to remember that once you've connected you're issuing commands to the shell, not to Ruby.
Net::SSH.start('host', 'ava') do |ssh|
`ruby /home/ava/test`
end
gives ruby: No such file or directory -- /home/ava/test (LoadError)
The best way to diagnose this is to start by SSHing to your own local box and executing the code locally, or using surrogate code that only echoes the commands you'd be using in real life on the other machine. Then you can test to see if the actions would be called.
Instead of:
ruby /home/ava/test
issue a command like:
ls -al /home/ava
first, to see what files are visible.
Follow that with something like:
ruby -pe '%x(ls /home/ava)'
to see if Ruby is found and it can execute that command in the path.
Dealing with remote systems isn't the same as running scripts locally. Your environment can be different, meaning your PATH or variables you expect might not be the same.

Related

With Ruby: how can I test to see if a linux command is available without returning the output of the command I'm testing for?

I'm using Ruby on Linux.
I'd like to test for the existence of a command on the Linux system.
I'd like to not get back the output of the command that I'm testing for.
I'd also like to not get back any output that results from the shell being unable to find the command.
I want to avoid using shell redirection from within the command that I send to the shell. So something like system("foo > /dev/null") would be unsuitable.
I'm ok with using redirection if there is a way to do it from Ruby.
The simplest thing would be just to use system. Let's say you're looking for ls.
irb(main):005:0> system("which ls")
/bin/ls
=> true
If that's off the table, you could peek into the directories in ENV["PATH"] for the executable you're looking for. ENV["PATH"].split(":") would give you an array of directory names to check for the desired command. If you find a file with the right name, you may want to ensure it's an executable.
I want to avoid using shell redirection from within the command that I
send to the shell. So something like system("foo > /dev/null") would
be unsuitable. I'm ok with using redirection if there is a way to do it from Ruby.
system("exec which cmd", out: "/dev/null")
puts "Command is available." if ($?).success?
The exec is to explicitly avoid unnecessary forking in the shell.
As a sidenote type -P can be used instead of which, but it relies on Bash and may have surprising effects if script is ported to an environment with a different default shell.

Convert Chef recipe to series of Bash commands?

Typically, one wants to convert Bash scripts to Chef. But sometimes (like, right now) you need to do the opposite. Is there an automatic way to get the list of commands run for a given Chef cookbook on a given configuration?
I'm not trying to end up with something with the full functionality of the Chef cookbook. I want to end up with a small set of commands that reproduce this particular installation on this particular environment. (The reason in this case is I need to separate out the 'sudo' commands and get them run by someone else. I do have sudo access on a machine that I could run Chef on to carry out this task though.)
I doubt you can do that in general, and even if you could, it would likely be more work than implementing what you need in Chef.
For example, even something as simple as creating a configuration file is implemented in Chef as ruby code; you would need to figure out a way to turn that into echo "…" > /etc/whatever.com. Doing that for all resources would be a major undertaking.
It seems to me that what you should actually do is modify any Chef cookbook that you use to run commands as a different user.
Things like template and file are pretty easy: the file will be created as root, and then chown-ed to the correct user. execute resources (which run commands) can be configured to run the command with su simply by specifying the user:
execute "something" do
command "whoami"
user "nobody"
end
It might take you a while to figure out, but once you get the hang of it it's pretty easy; much easier than converting to bash.

Piping stdin to ruby script via `myapp | myscript.rb`

I have an app that runs continuously, dumping output from a server and sending strings to stdout. I want to process this output with a Ruby script. The strings are \n-terminated.
For example, I'm trying to run this on the command line:
myapp.exe | my_script.rb
...with my_script.rb defined as:
while $stdin.gets
puts $_
end
I ultimately am going to process the strings using regexes and display some summary data, but for now I'm just trying to get the basic functionality hooked up. When I run the above, I get the following error:
my_script.rb:1:in `gets': Bad file descriptor (Errno::EBADF)
from my_script.rb:1
I am running this on Windows Server 2003 R2 SP2 and Ruby 1.8.6.
How do I continuously process stdin in a Ruby script? (Continuously as in not processing a file, but running until I kill it.)
EDIT:
I was able to make this work, sort of. There were several problems standing in my way. For one thing, it may be that using Ruby to process the piped-in stdin from another process doesn't work on Windows 2003R2. Another direction, suggested by Adrian below, was to run my script as the parent process and use popen to connect to myapp.exe as a forked child process. Unfortunately, fork isn't implemented in Windows, so this didn't work either.
Finally I was able to download POpen4, a RubyGem that does implement popen on Windows. Using this in combination with Adrian's suggestion, I was able to write this script which does what I really want -- processes the output from myapp.exe:
file: my_script.rb
require 'rubygems'
require 'popen4'
status =
POpen4::popen4("myapp.exe") do |stdout, stderr, stdin, pid|
puts pid
while s = stdout.gets
puts s
end
end
This script echoes the output from myapp.exe, which is exactly what I want.
Try just plain gets, without the $stdin. If that doesn't work, you might have to examine the output of myapp.exe for non-printable characters with another ruby script, using IO.popen.
gets doesn't always use stdin but instead tries to open a file.
See SO.
Try executing your Ruby script by explicitly calling ruby:
myapp.exe | ruby my_script.rb
I've experienced some odd behavior using stdin in Ruby when relying on Windows to invoke the correct program based on the file associations.

For ruby/webrick, I need windows to recognize shebang (#!) notation

(Bear with me, I promise this gets to shebang and windows.)
I have about the simplest of WEBRick servers put together:
require 'webrick'
include WEBrick
s = HTTPServer.new(:Port=>2000, :DocumentRoot=>Dir::pwd)
s.start
Couldn't be simpler. This basic server does accept http connections (firefox, internet exploder, wget, TELENT) and deals with them appropriately, as long as I'm just fetching static documents. If, however, I set one of the files in the directory to have a .cgi extension, I get a 500 back and the following on the server's terminal:
ERROR CGIHandler: c:/rubyCGI/test.cgi:
C:/...[snip]...webrick/httpservlet/cgi_runner.rb:45: in 'exec': Exec format error - ...[snip]...
I've done a few things on the command line to mimic what is going on in line 45 of cgi_runner.rb
c:\>ruby
exec "c:/rubyCGI/test.cgi"
^Z
(same error erupts)
c:\>ruby
exec "ruby c:/rubyCGI/test.cgi"
^Z
Content-type: text/html
Mares eat oats and does eat oats and I'll be home for Christmas.
Clearly, WEBrick hasn't been cleared for landing on windows. Your usual headaches of corporate paranoia prevent me from modifying webrick, so can I get the shebang notation in c:/rubyCGI/test.cgi recognized by the OS (windows) so I don't have to explicitly tell it each time which interpreter to use? I could assign all .cgi files to be associated with ruby, but that would be limiting in the long run.
UPDATE:
Since posting this, it has occurred to me that it may not be possible at all to run a cgi web server from ruby; ruby has no forking support. With no ability to fork a process, a cgi server would have to execute each cgi script one-at-a-time, neglecting all concurrent requests while the first one completed. While this may be acceptable for some, it would not work for my application. Nevertheless, I would still be very interested in an answer to my original question—that of getting shebang working under windows.
I think what you want is to associate the file extension with Ruby. I don't think it's possible to get the !# notation to work on Windows but it is possible to get Windows to automatically launch a script with a particular interpreter (as in your second example). A good step by step discussion of what you'd want to do is here. You specifically want the section headed: "To create file associations for unassociated file types". I think that will accomplish what you're trying to do.
A generic solution that works for both Ruby 1.8.6.pxxx and 1.9.1.p0 on
Windows is the following:
Edit the file: c:\ruby\lib\ruby\1.9.1\webrick\httpservlet\cgi_runner.rb
Add the following lines at the top of the file:
if "1.9.1" == RUBY_VERSION
require 'rbconfig' #constants telling where Ruby runs from
end
Now, locate the last line where is says: exec ENV["SCRIPT_FILENAME"]
Comment that line out and add the following code:
# --- from here ---
if "1.9.1" == RUBY_VERSION #use RbConfig
Ruby = File::join(RbConfig::CONFIG['bindir'],
RbConfig::CONFIG['ruby_install_name'])
Ruby << RbConfig::CONFIG['EXEEXT']
else # use ::Config
Ruby = File::join(::Config::CONFIG['bindir'],
::Config::CONFIG['ruby_install_name'])
Ruby << ::Config::CONFIG['EXEEXT']
end
if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
exec "#{Ruby}", ENV["SCRIPT_FILENAME"]
else
exec ENV["SCRIPT_FILENAME"]
end
# --- to here ---
Save the file and restart the webrick server.
Explanation:
This code just builds a variable 'Ruby' with the full path to
"ruby.exe", and
(if you're running on Windows) it passes the additional parameter
"c:\ruby\bin\ruby.exe" , to the Kernel.exec() method, so that your
script can be executed.
Not really to argue... but why bother webrick when mongrel is much faster and with native compiled with windows? And of coz, that means no shebang is needed.
Actually, it is possible to get Windows to recognize shebang notation in script files. It can be done in a relatively short script in say, Ruby or AutoIt. Only a rather simple parser for the first line of a script file is required, along with some file manipulation. I have done this a couple times when either cross-compatibilty of script files was required or when Windows file extensions did not suffice.

Determine if a ruby script is already running

Is there an easy way to tell if a ruby script is already running and then handle it appropriately? For example: I have a script called really_long_script.rb. I have it cronned to run every 5 minutes. When it runs, I want to see if the previous run is still running and then stop the execution of the second script. Any ideas?
The ps is a really poor way of doing that and probably open to race conditions.
The traditional Unix/Linux way would be to write the PID to a file (typically in /var/run) and check to see if that file exists on startup.
e.g. the pidfile being located at /var/run/myscript.pid then you'd check to see if that exists before running the program. There are a few tricks to avoid race conditions involving using O_EXCL (exclusing locking) to open the file and symbolic links.
However unlikely, you should try to code to avoid race conditions by using atomic operations on the filesystem.
To save re-inventing the wheel, you might want to look at http://rubyforge.org/projects/pidify/
Highlander
Description
A gem that ensures only one instance of your main script is running.
In short, there can be only one.
Installation
gem install highlander
Synopsis
require 'highlander' # This should be the -first- thing in your code.
# Your code here
Meanwhile, back on the command line...
# First attempt, works. Assume it's running in the background.
ruby your_script.rb
# Second attempt while the first instance is still running, fails.
ruby your_script.rb # => RuntimeError
Notes
Simply requiring the highlander gem ensures that only one instance
of that script cannot be started again. If you try to start it again
it will raise a RuntimeError.
You should probably also check that the process is actually running, so that if your script dies without cleaning itself up, it will run the next time rather than simply checking that
/var/run/foo.pid exists and exiting.
In bash:
if ps aux | grep really_long_script.rb | grep -vq grep
then
echo Script already running
else
ruby really_long_script.rb
fi

Resources