Require a file to be called from the command line - ruby

So I know how to use optparser to use the command line to call a specific method in my program. But, is there a way to use optparse where the user is required to specify a file in order to have the command work? Like for example when using this code:
test.rb
#!/usr/bin/ruby
read = File.readlines(file)
puts read
The user would be required to specify a specific file the program needs to read.
test.rb -b test.txt
Is there a way to do this or am I still too new to ruby to fully understand how it works?

I don't know about optparse, but you can do something like this perhaps:
#test.rb
#!/usr/bin/ruby
file = ARGV[0]
read = File.readlines(file)
puts read
then run the file in command line, passing the file name as an argument. Where ARGV[0] corresponds with the first argument passed:
$ ruby test2.rb test2.rb
##test.rb
##!/usr/bin/ruby
#file = ARGV[0]
#read = File.readlines(file)
#puts read

Related

How do I run a program via Ruby and access its standard input and output

I have a completely separate Ruby file that reads from Standard Input and writes to Standard Output.
I have certain test cases that I want to try. How do I pass my inputs to Standard Input to the file, and then test the Standard Output against the expected results?
As an example, here's the stuff I've already figured out:
There's a file that reads a number from standard input, squares it, and writes it to standard input
square.rb:
#!/usr/local/bin/ruby -w
input = STDIN.read
# square it
puts input.to_i ** 2
Complete the pass_input_to_file method of test.rb:
require 'minitest/autorun'
def pass_input_to_file(input)
# complete code here
end
class Test < Minitest::Test
def test_file
assert_equal pass_input_to_file(2), 4
end
end
You can use the Ruby Open3 Library to submit STDIN when calling a ruby script.
require 'open3'
def pass_input_to_file(input)
output, _status = Open3.capture2('path_to_script', :stdin_data => input)
output
end
The easiest way to test this would probably be to have your program look to see if it was passed any arguments first. Something like this:
#!/usr/local/bin/ruby -w
input = ARGV[0] || STDIN.read
# square it
puts input.to_i ** 2
and then you can shell out to test it:
def pass_input_to_file(input)
`path/to/file #{input}`.to_i
end
Otherwise, I would reach for something like expect to automate a subshell.
As an aside, for more complicated programs, using OptionParser or a CLI gem is probably better than looking at ARGV directly.

delivering executable with gem

I am trying to deliver an exec file with my ruby gem but am not sure how to go about it.
I have a ruby script called pfparser like:
#!/usr/bin/env ruby
ENV['path'] = ARGV[0]
require 'pfparser'
in my pfparser.rb file I want to accept the parameter but it doesn't seem to pass over. The parameter should be a file path that the pfparser.rb file should then parse and return an output. I feel like I am not doing this correctly but I am not sure how to go about it.
This feels like overkill, I want to deliver one file that the user can then execute with a parameter.
I assume pfparser.rb contains a class definition, that handles all the stuff. So I would go with something like that:
lib/pfparser.rb
class PfParser
def initialize path
#path = path
end
def do_job
# change to real stuff
puts "Hi, path is: #{#path}"
end
end
bin/pfparser
#!/usr/bin/env ruby
require 'pfparser'
PfParser.new(ARGV[0]).do_job
And now:
$ bin/pfparser PATH
#⇒ Hi, path is: PATH

Getting Console Input with Ruby's Docopt

I'm using Docopt in Ruby to parse my command options, and later in the script I am getting console input using gets.chomp. The problem is that all of the args from the running the program are still left in ARGF after Docopt does its parsing with options = Docopt::docopt(doc), and doing a gets command takes from ARGF before it tries gets'ing from STDIN.
I've tried to clear ARGF, but doing ARGF.gets for some reason tries to run the input as a command. I think clearing ARGF or using another input method could both be solutions, but I haven't found anything yet. I have to imagine that I'm not the first to try to get interactive command line input in Ruby with Docopt, so I'm hoping the answer is out there.
Some more code for those who would like it:
#!/usr/bin/ruby
require 'docopt'
doc=<<eos
Usage:
account_creator.rb --noldap [options]
Options:
-h, --help Show help page
-l, --ldap
-n, --noldap
-s SERVER With or without http[s]://, followed by port
--ad
--od
-d NUM
-u USER
-p PASS
-o OUTPUT-FILE Default behavior is to append output to file if it exists
eos
options = {}
begin
options = Docopt::docopt(doc)
rescue Docopt::Exit => e
puts e.message
exit 1
end
if options['-u']
username = options['-u']
else
while username.eql? '' or username == nil
puts "Enter Username:"
username = Kernel.gets.chomp.strip
end
end
This is unrelated to docopt. Try it on its own:
$ cat test.rb
#!/usr/bin/ruby
puts "Enter Username:"
username = gets
$ ./test.rb something
Enter Username:
./test.rb:4:in `gets': No such file or directory - something (Errno::ENOENT)
from ./test.rb:4:in `gets'
from ./test.rb:4:in `<main>'
Kernel.gets in ruby uses ARGF.gets. Using STDIN.gets should get you your expected behavior. See this SO question.

Running Ruby scripts from command line

I have a 2 scripts:
test1.rb
require 'test2.rb'
puts "hello"
test2.rb
puts "test"
I'm running this by executing ruby test2.rb test1.rb.
But only test is printed out and not hello.
You only need to run ruby test1.rb and the require statement should pull in test2.rb for you - you don't need to put it on the command line as well. (That will try and run test2.rb, passing the string 'test1.rb' as an argument, which is not what you want here)
Edit: the require statement does not look in the current directory by default when trying to find 'test2.rb'. You can explicitly specify it by changing it to:
require File.dirname(__FILE__) + '/test2.rb'
in test1.rb do (assuming test2.rb is in same directory, otherwise give its path relative to test1.rb)
require_relative 'test2.rb'
puts "hello"
and on the command line just do ruby test1.rb
This should work as well
require './test2.rb'
puts "hello"
There are some explanation how you can solve your problem, but not what is going wrong.
With ruby test2.rb test1.rb you call the ruby script with the parameter test1.rb.
You have access to the parameters in the constant ARGV.
An example with this script:
puts "test"
puts 'ARGV= %s' % ARGV
The result when you call it:
C:\Temp>ruby test.rb test2.rb
test
ARGV= test2.rb
So you could also write a program like:
require_relative ARGV.first
The first parameter defines a script to be loaded.
Or if you want to load many scripts you could use:
ARGV.each{|script| require_relative script }

How to read an open file in Ruby

I want to be able to read a currently open file. The test.rb is sending its output to test.log which I want to be able to read and ultimately send via email.
I am running this using cron:
*/5 * * * /tmp/test.rb > /tmp/log/test.log 2>&1
I have something like this in test.rb:
#!/usr/bin/ruby
def read_file(file_name)
file = File.open(file_name, "r")
data = file.read
file.close
return data
end
puts "Start"
puts read_file("/tmp/log/test.log")
puts "End"
When I run this code, it only gives me this output:
Start
End
I would expect the output to be something like this:
Start
Start (from the reading of the test.log since it should have the word start already)
End
Ok, you're trying to do several things at once, and I suspect you didn't systematically test before moving from one step to the next.
First we're going to clean up your code:
def read_file(file_name)
file = File.open(file_name, "r")
data = file.read
file.close
return data
end
puts "Start"
puts read_file("/tmp/log/test.log")
puts "End"
can be replaced with:
puts "Start"
puts File.read("./test.log")
puts "End"
It's plain and simple; There's no need for a method or anything complicated... yet.
Note that for ease of testing I'm working with a file in the current directory. To put some content in it I'll simply do:
echo "foo" > ./test.log
Running the test code gives me...
Greg:Desktop greg$ ruby test.rb
Start
foo
End
so I know the code is reading and printing correctly.
Now we can test what would go into the crontab, before we deal with its madness:
Greg:Desktop greg$ ruby test.rb > ./test.log
Greg:Desktop greg$
Hmm. No output. Something is broken with that. We knew there was content in the file previously, so what happened?
Greg:Desktop greg$ cat ./test.log
Start
End
Cat'ing the file shows it has the "Start" and "End" output of the code, but the part that should have been read and output is now missing.
What happening is that the shell truncated "test.log" just before it passed control to Ruby, which then opened and executed the code, which opened the now empty file to print it. In other words, you're asking the shell to truncate (empty) it just before you read it.
The fix is to read from a different file than you're going to write to, if you're trying to do something with the contents of it. If you're not trying to do something with its contents then there's no point in reading it with Ruby just to write it to a different file: We have cp and/or mv to do those things for us witout Ruby being involved. So, this makes more sense if we're going to do something with the contents:
ruby test.rb > ./test.log.out
I'll reset the file contents using echo "foo" > ./test.log, and cat'ing it showed 'foo', so I'm ready to try the redirection test again:
Greg:Desktop greg$ ruby test.rb > ./test.log.out
Greg:Desktop greg$ cat test.log.out
Start
foo
End
That time it worked. Trying it again has the same result, so I won't show the results here.
If you're going to email the file you could add that code at this point. Replacing the puts in the puts File.read('./test.log') line with an assignment to a variable will store the file's content:
contents = File.read('./test.log')
Then you can use contents as the body of a email. (And, rather than use Ruby for all of this I'd probably do it using mail or mailx or pipe it directly to sendmail, using the command-line and shell, but that's your call.)
At this point things are in a good position to add the command to crontab, using the same command as used on the command-line. Because it's running in cron, and errors can happen that we'd want to know about, we'd add the 2>&1 redirect to capture STDERR also, just as you did before. Just remember that you can NOT write to the same file you're going to read from or you'll have an empty file to read.
That's enough to get your app working.
class FileLineRead
File.open("file_line_read.txt") do |file|
file.each do |line|
phone_number = line.gsub(/\n/,'')
user = User.find_by_phone_number(line)
user.destroy unless user.nil?
end
end
end
open file
read line
DB Select
DB Update
In the cron job you have already opened and cleared test.log (via redirection) before you have read it in the Ruby script.
Why not do both the read and write in Ruby?
It may be a permissions issue or the file may not exist.
f = File.open("test","r")
puts f.read()
f.close()
The above will read the file test. If the file exists in the current directory
The problem is, as I can see, already solved by Slomojo. I'll only add:
to read and print a text file in Ruby, just:
puts File.read("/tmp/log/test.log")

Resources