rvm or the Ruby Version Manager, works perfectly fine when there is a single version of Ruby to be run on the system (among multiple versions installed, of course) around the entire system. In a production scenario, updating ruby itself is a task which is not taken lightly!

This worked perfectly until I encountered two quite different situations –

One scenario was where I wanted to execute a command via ssh into the server. So something like –

ssh user1@server1 'ruby ~/program.rb'

The other being when I wanted to run a ruby program via cron

In both these cases, I kept getting the error rvm not found. Initially, I couldn’t understand this at all, because every time I logged in and ran the program or the command it would run prefectly.

And then I learnt about the difference between a login and a non-login shell.

A login shell is created after a successful login of a user or via the switch user su - user1 command.

This executes the following in order –

  1. executes /etc/profile
  2. /etc/profile executes all scripts in /etc/profile.d/
  3. executes the user’s ~/.bash_profile
  4. ~/.bash_profile executes the user’s ~/.bashrc
  5. ~/.bashrc executes `/etc/bashrc`

A non-login shell, on the other hand, executes the following in order –

  1. executes the user’s ~/.bashrc
  2. executes /etc/bashrc
  3. /etc/bashrc calls the scripts in /etc/profile.d

A key thing to note here is that the user’s .bashrc gets executed in both the steps.

Now, one of the lines right at the top of the .bashrc script is this –

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Since a non-login shell is not running interactively, the return gets executed. Anything further down, doesn’t.

For rvm, we need to source the following in our .bashrc file –

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

If this line is added at the bottom of the .bashrc, then the non-interactive shell never gets to source rvm.

Because of this, it should always be added towards the top of the .bashrc file before the check for the non-interactive shell.

I added an additional comment to indicate why it is there –

# IMPORTANT - load rvm for non-login shells e.g. Net:SSH
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

# If not running interactively, don't do anything
[ -z "$PS1" ] && return