Invoking bash from Ruby

I’m a novice at Ruby, although I’ve been introduced to its syntax by
virtue of working with Puppet for the past 2 1/2 years. As a sys admin,
I’m most comfortable working with bash, but I recognize that there are
things that a
scripting language can’t do - like interacting with a database. So I’m
writing some Ruby code to extract text out of an HTML file and then
insert it into a mySQL database. I’ve been using Nokogiri to get some
formatted text, but also need to get some text that’s easy enough to get
with a scripting language like bash. Specifically, I need to do this:

grep “Date:” $1 | cut -d’ ’ -f3-5

Is there an easy way to embed this logic within Ruby code - or, better
yet - a way to do the same thing in Ruby?

There are a number of ways of executing system code from Ruby. As you
are familiar with Bash, the easiest would probably be to use a pair of
backticks “``” to execute code. Ruby returns the output as a string.

date = `grep "<strong>Date:</strong>" $1 | cut -d' ' -f3-5`

You may also use the executable string notation “%x{}” which is similar
to Bash’s “$()” notation.

date = %x{grep "<strong>Date:</strong>" $1 | cut -d' ' -f3-5}

You can check the exit status using “$?” as you would in Bash as well.
However, an object is returned so you will want to compare
“$?.exitstatus”.

There are also commands like “system”, “exec”, “spawn”, etc., which make
other shell features accessible.

Jamal Wills

Thanks for the quick reply! I’ve substituted $1 with ARGV[0], as I
believe that’s proper Ruby syntax for a command argument, but I’m still
having trouble getting the grep command to treat the argument as a file
name.

Here’s the entirety of my code:

#!/usr/bin/env ruby

fn = ARGV[0]
puts fn
date = %x{grep “Date:” fn | cut -d’ ’ -f3-5}
puts date

And here’s the result when I run it on the command line:

pablo@pmenaws1=> ./extract_date_written.rb myfile.html
myfile.html
grep: fn: No such file or directory

I’m hoping this is a newbie mistake that is easily remedied. Thanks
again in advance.

Yes; use string interpolation to insert the variable’s content into the
string:

foo = ‘world’
puts “Hello, #{foo}!” # outputs: Hello, world!

It works the same for backticks and the percent string-like notation
(but you’ll have to use %X with uppercased X, similarly to how you
have to use strings in quotes instead of in apostrophes to be able to
interpolate in them).

So this would be (note: code untested):

date = %X{grep “Date:” #{fn} | cut -d’ ’ -f3-5}

Also, if the contents of fn come from the user, you should escape it
somehow to prevent them from executing arbitrary commands in your
shell environment!

– Matma R.

I knew someone would set me straight. Thank you so much!

By the way, ultimately the contents of “fn” will come from a script and
not from a user, so there should be no potential security holes.

Paul M. wrote in post #1089118:

So I’m
writing some Ruby code to extract text out of an HTML file and then
insert it into a mySQL database. I’ve been using Nokogiri to get some
formatted text, but also need to get some text that’s easy enough to get
with a scripting language like bash. Specifically, I need to do this:

grep “Date:” $1 | cut -d’ ’ -f3-5

Is there an easy way to embed this logic within Ruby code - or, better
yet - a way to do the same thing in Ruby?

data.txt:

Testing Hello world
no no no no no no
Date:
1 2 3 4 5 6 7
Date:
a b c d e f g

my_prog.rb:

fname = ARGV[0]
start_column = 3
end_column = 5

target_range = (start_column-1)…(end_column-1)

IO.foreach(fname) do |line|
if line.match(/Date:</strong>/)
pieces = line.split(" ")

puts pieces[target_range].join(":")

end
end

–output:–
$ ruby my_prog.rb data.txt
3:4:5
c:d:e

So I’m
writing some Ruby code to extract text out of an HTML file and then
insert it into a mySQL database. I’ve been using Nokogiri to get some
formatted text, but also need to get some text that’s easy enough to get
with a scripting language like bash.

Nokogiri provides myriad ways to locate any text in an html file.
For instance:

require ‘nokogiri’

fname = ‘data.txt’
@doc = Nokogiri::XML(File.open(fname))

my_xpath ="//strong[text()=‘Date:’]/following-sibling::div[1]"

@doc.xpath(my_xpath).each do |div|
puts div.text.split(" “)[2…4].join(”:")
end

–output:–
$ ruby my_prog.rb
3:4:5
c:d:e

I recognize that there are
things that a
scripting language can’t do - like interacting with a database.

What scripting language is unable to interact with a database?

mysql> use mydb;
mysql> select * from people;
±—±------±------+
| id | name | info |
±—±------±------+
| 1 | Jane | 3 4 5 |
| 2 | John | a b c |
±—±------±------+

require ‘mysql2’

begin
client = Mysql2::Client.new(:host => “localhost”, :username => “root”)

client.query(“USE mydb”)
results = client.query(“SELECT * FROM people”)

results.each do |row|
puts “#{row[‘id’]} #{row[‘name’]} #{row[‘info’]}”
end

client.query(“INSERT INTO people(name, info) VALUES(‘Jeff’, ‘7 8 9’)”)
results = client.query(“SELECT * FROM people”)

results.each do |row|
puts “#{row[‘id’]} #{row[‘name’]} #{row[‘info’]}”
end

rescue Mysql2::Error => e
puts e.errno
puts e.error
ensure
client.close if client
end

–output:–
1 Jane 3 4 5
2 John a b c

1 Jane 3 4 5
2 John a b c
3 Jeff 7 8 9

mysql> select * from people;
±—±------±------+
| id | name | info |
±—±------±------+
| 1 | Jane | 3 4 5 |
| 2 | John | a b c |
| 3 | Jeff | 7 8 9 |
±—±------±------+

Thanks again for numerous suggestions. I’m learning that Ruby is a rich
and versatile language.

Am 14.12.2012 19:15, schrieb Paul M.:

grep “Date:” $1 | cut -d’ ’ -f3-5

Is there an easy way to embed this logic within Ruby code - or, better
yet - a way to do the same thing in Ruby?

Yes, you can do this easily without using Bash:

pattern = %r{Date:}
File.readlines(‘testfile.txt’).grep(pattern).each do |line|
p line.split(’ ')[2…4]
end

Output:

[“2”, “3”, “4”]
[“b”, “c”, “d”]

where:

$ cat testfile.txt
Date: 1 2 3 4 5
line
another line without Date
Date: a b c d e
yet another line