7

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

10
  • 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, 2015 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. Mar 25, 2015 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, 2015 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, 2015 at 19:02
  • @jordanm I've edited the post. The issue with being "primarily opinion-based" should be resolved.
    – Dylan Cali
    Mar 25, 2015 at 23:49

2 Answers 2

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
1
  • 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, 2015 at 19:40
2

bash does read ~/.bashrc though even when non-interactive when invoked over ssh when compiled with #define SSH_SOURCE_BASHRC as it is on Debian and derivatives at least (a misfeature IMO, but comes 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
1
  • Confirmed it works for me. The first command in stock .bashrc is usually preceded by a comment # If not running interactively, don't do anything. If you put anything before that line, it will be executed before command when running ssh host command.
    – raj
    Jul 12, 2022 at 15:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .