views:

267

answers:

1

Hi,

I'm trying to setup a simple timer that gets started from a Rails Application. This timer should wait out its duration and then start a shell script that will start up ./script/runner and complete the initial request. I need script/runner because I need access to ActiveRecord.

Here's my test lines in Rails

output = `at #{(Time.now + 60).strftime("%H:%M")} < #{Rails.root}/lib/parking_timer.sh STRING_VARIABLE`
return render :text => output

Then my parking_timer.sh looks like this

#!/bin/sh               
~/PATH_TO_APP/script/runner -e development ~/PATH_TO_APP/lib/ParkingTimer.rb $1
echo "All Done"    

Finally, ParkingTimer.rb reads the passed variable with

ARGV.each do|a|
   puts "Argument: #{a}"
end

The problem is that the Unix command "at" doesn't seem to like variables and only wants to deal with filenames. I either get one of two errors depending on how I position "s

If I put quotes around the right hand side like so

... "~/PATH_TO_APP/lib/parking_timer.sh STRING_VARIABLE"

I get,

-bash: ~/PATH_TO_APP/lib/parking_timer.sh STRING_VARIABLE: No such file or directory

I I leave the quotes out, I get,

at: garbled time

This is all happening on a Mac OS 10.6 box running Rails 2.3 & Ruby 1.8.6

I've already messed around w/ BackgrounDrb, and decided its a total PITA. I need to be able to cancel the job at any time before it is due.

+1  A: 

After playing around with irb a bit, here's what I found.

The backtick operator invokes the shell after ruby has done any interpretation necessary. For my test case, the strace output looked something like this:

execve("/bin/sh", ["sh", "-c", "echo at 12:57 < /etc/fstab"], [/* 67 vars */]) = 0   

Since we know what it's doing, let's take a look at how your command will be executed:

/bin/sh -c "at 12:57 < RAILS_ROOT/lib/parking_timer.sh STRING_VARIABLE"

That looks very odd. Do you really want to pipe parking_timer.sh, the script, as input into the at command?

What you probably ultimately want is something like this:

/bin/sh -c "RAILS_ROOT/lib/parking_timer.sh STRING_VARIABLE | at 12:57"

Thus, the output of the parking_timer.sh command will become the input to the at command.

So, try the following:

 output = `#{Rails.root}/lib/parking_timer.sh STRING_VARIABLE | at #{(Time.now + 60).strftime("%H:%M")}`
return render :text => output

You can always use strace or truss to see what's happening. For example:

strace -o strace.out -f -ff -p $IRB_PID

Then grep '^exec' strace.out* to see where the command is being executed.

Kaleb Pederson
I was able to get it to at least compile and run without spitting back junk at me with`at -t\"#{(Time.now + 60).strftime("%m%d%H%M")}\" < "#{Rails.root}/lib/parking_timer.sh" "4"`However it doesn't pass the variable whereas calling the script directly, it does.sh parking_timer.sh 4It doesn't seem to matter if I have the quotes around 4 or not or around the script path.
Andrew
I made some major changes after doing some investigation.
Kaleb Pederson
Thanks, got it working with thisoutput = `#{Rails.root}/script/runner -e development #{Rails.root}/lib/ParkingTimer.rb #{4.to_s} | at -t#{(Time.now + 60).strftime("%m%d%H%M")}`It completely bypasses the .sh script but so what.output is still blank, which will likely cause problems for me down the road, (can't find the job to delete if need be).
Andrew
Nevermind the bit about not being able to get the output. I can just run another output = `at -l` command immediately after and grab the job number by splitting the output... arr = output.split(" ") job_num = arr[arr.length - 6]
Andrew