#!/usr/bin/env ruby is not found in cron - ruby

I have a simple ruby script, hello.rb:
#!/usr/bin/env ruby
puts 'hello'
It runs ok at the command line:
# /usr/local/src/hello/hello.rb
hello
However, if I put it in cron:
* * * * * /usr/local/src/hello/hello.rb >> /usr/local/src/hello/hello.log 2>&1
There are errors in the log file:
/usr/bin/env: ruby: No such file or directory
/usr/bin/env: ruby: No such file or directory
...
/usr/bin/env: ruby: No such file or directory
/usr/bin/env ruby runs ok at command line though:
# /usr/bin/env ruby -v
ruby 1.8.7 (2012-10-12 patchlevel 371) [i686-linux]
How to fix the env error for cron?

The problem is that the environment isn't what you expect.
You don't say whether the cron is running as your user, or as root, but, in either case, you can test to see what the environment looks like by adding another cron entry of:
* * * * * /usr/bin/env > /path/to/your/home/directory/env.txt
Let that run once, then pull it out, and look at the file.
Instead of using /usr/bin/env to try to find a Ruby to run your code, define the Ruby explicitly:
* * * * * /path/to/the/ruby/you/want /usr/local/src/hello/hello.rb >> /usr/local/src/hello/hello.log 2>&1
You can figure out which Ruby you want by using:
which ruby
Alternately, instead of relying on /usr/bin/env in your #! line, define your Ruby there.
Using /usr/bin/env ruby in your code is a convenience when you're using something like RVM or rbenv, and switching between versions of Ruby. It's not a good choice when you're putting something into "production", whether it's on your machine in your own account, or on a production host running as root.
If you are on Linux or Mac OS, try man 5 crontab for more information. Also, "Where can I set environment variables that crontab will use?" should be very useful.

env searches only in the existing PATH variable. crond creates the process that is run as your user name. So the PATH is minimal. You have to set up your environment variables in the script itself

Related

Cron is running in home directory instead of file directory

I followed some other posts in stackoverflow and successfully setup cron with RVM using rvm cron setup and injected some ENV to the crontab file.
When I was troubleshooting why the dotenv gem is not working, I realised the following:
I placed my test.rb in file path /home/myuser/ruby/test.rb and had my crontab file as shown below:
* * * * * ruby /home/myuser/ruby/test.rb >> /home/myuser/ruby/output.log
and when I puts the output of the test.rb with Dir.pwd. The output states that the rb is run in the /home/myuser/ directory instead of /home/myuser/ruby directory.
While I had a hotfix by manually changing the path. But I wonder why it is the case.
By default, cron tasks of a user are executed from the user's home directory. In order to execute the script from proper directory, you have to "cd" to it.
Consider changing your crontab to:
* * * * * cd /home/myuser/ruby && ruby ./test.rb >> /home/myuser/ruby/output.log
Good luck!
According to #Pawel Dawczak who left the answer in the comment.
the solution is to rewrite the statement in crontab as
* * * * * cd /home/myuser/ruby && ruby test.rb >> /home/myuser/ruby/output.log
Thanks!

why bash -l -c "CMD" makes ruby find my gem?

In my ruby script,I required the gmail gem:
require 'rubygems'
require 'gmail'
when running in shell,it works ok:
ruby my-script.rb
while when I put it in a cron job,it failed to execuate:
* * * * * cd /to/script/directory;/usr/local/rvm/rubies/ree-1.8.7-2011.03/bin/ruby ./my-script.rb
the log shows that the gmail gem can not be loaded:
/usr/local/rvm/rubies/ree-1.8.7-2011.03/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require': no such file to load -- gmail (LoadError)
well, when i do this(put the cmd in bash -l -c 'CMD'):
* * * * * /bin/bash -l -c 'cd /to/script/directory;/usr/local/rvm/rubies/ree-1.8.7-2011.03/bin/ruby ./my-script.rb'
it works ok again.
why?
ps.I know the arg -l make the bash a login shell,but does that make any difference?
The -l parameter executes the command in a login shell, which means that it inherits your path and other settings from your shell profile. The cron job, if run without the login shell, will be run without any path environment variables set (such as those set by RVM), which results in the system being unable to find the referenced gems.
rvm requires you use a shell login, see FAQ.
I suspect you are loading RVM in your login script, ergo RVM will only be available in a login shell.

Can the whenever gem preserve existing lines in a crontab file?

I am using:
Ruby 1.9.2
whenever 0.7.2
capistrano 2.9.0
capistrano-ext 1.2.1
I am using whenever in conjunction with Capistrano on deploys to manage my crontab files.
I noticed that it completely rewrites my crontab files each time.
I'd like to be able to set environment variables in cron to control PATH and MAILTO settings, which are regular cron environment variables.
Is there a way to make whenever not overwrite the entire crontab file, so that I can add customizations to my crontab file and be sure that they will persist?
Yes, you can do this. You'll just need to assign an identifier to the task being written to crontab:
whenever --update-crontab some_identifier_name
It will generate an entry in crontab like this:
# Begin Whenever generated tasks for: some_identifier_name
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /bin/bash -l -c 'cd /var/www/test/releases/20120416183153 && script/rails runner -e production '\''Model.some_method'\'' >> /tmp/cron_log.log 2>&1'
# End Whenever generated tasks for: some_identifier_name
Then whenever you call the command above it will only update where it finds the identifier you specified.

Setting path for whenever in cron so it can find ruby

My ruby is in /usr/local/bin. whenever can't find it, and setting PATH at the top of my cron file doesn't work either, I think because whenever is running the command inside of a new bash instance.
# this does not work
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin
# Begin Whenever generated tasks for: foo
0 * * * * /bin/bash -l -c 'cd /srv/foo/releases/20110429110637 && script/rails runner -e production '\''ActiveRecord::SessionStore::Session.destroy_recent(15)'\'''
# End Whenever generated tasks for: foo
How can I tell whenever where my ruby binary is? Making a symbolic link from /usr/bin seems messy to me, but I guess that might be the only option.
This question offers env :PATH, "..." in schedule.rb as a solution, but (a) I can't find any documentation of that feature anywhere in the docs (b) it doesn't seem to have solved the asker's problem (unfortunately it takes non-trivial turnaround time for me to just try it).
update actually it is in the bottom of this page, i'll try it now.
more info
I can't modify the cron command because it's generated by whenever
i verified that if I make a new bash shell with bash -l, /usr/bin/env finds ruby just fine
I just tried the exact command in cron, starting with /bin/bash, from the command line of that user, and it worked.
so, this is very mysterious...
The solution is to put this in schedule.rb:
env :PATH, ENV['PATH']
Here's a little guide I put together on the topic.
rewrite your crontab as
0 * * * * { PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin ; export PATH ;/bin/bash -l -c 'cd /srv/foo/releases/20110429110637 && script/rails runner -e production '\''ActiveRecord::SessionStore::Session.destroy_recent(15)'\''' ; }
Or you should try to figure out why your BASH shell is not picking the PATH=... that is almost certainly in your .profile or .bash_profile.
I hope this helps.
As John Bachir pointed out, you can do it via env. But let me add more input. I am deploying on AWS Opsworks. Unfortunately they do not have a ruby manager (RVM, Rbenv, etc) installed by default.
The first thing I needed to do was SSH into the instance and figure out which ruby I was using. This was easy enough by executing the which ruby command in a terminal.
$ which ruby
/usr/local/bin/ruby
Cron was using ruby located at /usr/bin/ruby. This needed to be changed.
In schedule.rb, I have:
set :env_path, ''
env :PATH, #env_path if #env_path.present?
In local, env_path doesn't need to be set. For most users, the only thing to do is execute whenever as such:
bundle exec whenever --set 'environment=development' --update-crontab
On a staging / production environment, ruby may be installed elsewhere. So running this may be more appropriate:
bundle exec whenever --set 'environment=staging&env_path=/usr/bin/local' --update-crontab
You will need to replace /usr/bin/local with the output of echo $PATH.
In Opsworks, however, I needed to create a custom Chef recipe that looked like:
node[:deploy].each do |application, deploy|
execute 'whenever' do
user 'deploy'
group 'nginx'
cwd "#{deploy[:deploy_to]}/current"
command "bundle exec whenever --set 'environment=#{deploy[:environment_variables][:RAILS_ENV]}&env_path=#{ENV['PATH']}' --update-crontab"
end
end
I hope the information here is clear enough.

Cron Ubuntu does not fire ruby method

crontab -l gives me this
0,2,4,6,8,10 * * * * /bin/bash -l -c 'cd /home/ruben/Monitoring ; script/rails runner Ping.check_pings'
Why does this not work?
If i try "cd /home/ruben/Monitoring ; script/rails runner Ping.check_pings" in the command line it works. I have also tried it with "&&" as ";"
The problem may be related to PATH, or to some other environment variable (like GEM_HOME), that is defined properly in your command-line environment, but not in cron's environment.
crontab doesn't run with the enviroment of the user, rather it creates it's own slimmed down enviroment. This includes very small PATH - /usr/bin:/usr/sbin:. and some other variables. See more here - http://adminschoice.com/crontab-quick-reference
Easiest solution is to add '. ~/.profile' before you run rails, or to fix path in some other way.
BTW, before you try to add PATH=/my/path/here;$PATH into crontab - that syntax (variable expansion) is not allowed either

Resources