5

I would like run remote ssh commands and have them always load server-side startup files by default. I am looking for a solution that does not require:

  • configuration by root
  • adding extra boiler plate to the command each time
  • duplicating environment variables across multiple files

I am not looking to transport local environment variables to the remote shell. I want to run a remote command using the vanilla ssh <host> <command> syntax and have it run using the same environment a remote login session would get.

Background

The below assumes bash is being used for simplicity

By default, remote ssh commands start a non-interactive, non-login shell. Quoting the man page:

If command is specified, it is executed on the remote host instead of a login shell.

You can see this more explicitly on the shell by running:

# the lack of 'i' indicates non-interactive
$ ssh localhost 'echo $-'
hBc 

$ ssh localhost 'shopt login_shell'
login_shell     off

But what if your remote command needs certain environment variables set? Being a non-interactive, non-login shell means neither .bash_profile nor .bashrc will be sourced.

This is problematic, for example, if you're using tools like perlbrew or virtualenv and want to use your custom interpreter in the remote command:

$ which perl
/home/calid/perl5/perlbrew/perls/perl-5.20.1/bin/perl

$ ssh localhost 'which perl'
/usr/bin/perl

Solutions that do not satisfy the requirements above

Explicitly invoke a login shell in the remote command

$ ssh localhost 'bash --login -c "which perl"'

Requires extra boiler plate each time

Explicitly source your profile before the command

$ ssh localhost 'source ~/.bash_profile && which perl'

Requires extra boiler plate each time

Set environment variables in ~/.ssh/environment

Requires root to enable this functionality on the server

Duplicates environment variables already set in server startup files

Set ENV and BASH_ENV

Does not work: .bash_profile and .bashrc aren't sourced

Setting in ~/.ssh/environment works, but requires root access to enable

Preface the command with the environment variables you need

Requires extra boiler plate each time (potentially a lot)

Duplicates environment variables already set in server startup files

Related posts

https://stackoverflow.com/questions/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

https://stackoverflow.com/questions/216202/why-does-an-ssh-remote-command-get-fewer-environment-variables-then-when-run-man#216204

dot file not sourced when running a command via ssh

  • While I think this is a nice and informative question, I do not think it is a good fit for the site. "Best" is going to subjective. – jordanm Mar 25 '15 at 18:56
  • @calid, simplify your question to "how to transport my environment to a remote ssh session", and then provide an answer with the possibilities. That would be a useful question. – glenn jackman Mar 25 '15 at 18:58
  • @jordanm point taken on Best, I've replaced it with Elegant. In terms of subjective question in general, all the solutions (at least I know of) are objectively cumbersome simply in terms of required setup or boiler plate required.. would be nice to know if there's any solution that is less cumbersome. – Dylan Cali Mar 25 '15 at 19:00
  • @glennjackman I feel like that has largely been covered elsewhere (see for example the second link in Related Posts). I'd really like to know if there's a simple solution to this problem. – Dylan Cali Mar 25 '15 at 19:02
  • @jordanm I've edited the post. The issue with being "primarily opinion-based" should be resolved. – Dylan Cali Mar 25 '15 at 23:49
2

Create a wrapper shell function sshc which prefixes the source ~/.bash_profile boiler plate for you:

function sshc {
    local host=$1
    local cmd=$2

    ssh $host "source ~/.bash_profile && $cmd"
}

You can then use this as:

$ sshc localhost 'which perl'
/home/calid/perl5/perlbrew/perls/perl-5.20.1/bin/perl
  • Just occurred to me as I was walking over to get my coffee. The only downside is this is a client-side thing, not a one-time server-side solution. – Dylan Cali Mar 25 '15 at 19:40
0

bash does read ~/.bashrc though even when non-interactive when invoked over ssh (a misfeature IMO, but would come handy to you here).

So you could add to the top of ~/.bashrc on the remote host:

if [ -n "$SSH_CLIENT" ] &&
   [ "$SHLVL" = 0 ] &&
   [ -n "${-##*[il]*}" ]; then
   . /etc/profile
   . ~/.bash_profile
fi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.