37

Assuming user has /bin/bash as the shell in /etc/passwd. Then ssh user@host command runs the command using Bash. However, that shell is neither login nor interactive, which means neither ~/.bash_profile nor ~/.bashrc is sourced. In that case how to set the PATH environment variable so that executables can be found and executed? Is it recommended to prefix the actual command with source ~/.bashrc?


Edit. This question is trivial for Bash, because (as people pointed out) ~/.bashrc is sourced in such case. The definitive answer comes from this paragraph in man bash:

Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable. It will not do this if invoked as sh. The --norc option may be used to inhibit this behavior, and the --rcfile option may be used to force another file to be read, but neither rshd nor sshd generally invoke the shell with those options or allow them to be specified.

1
  • 12
    .bashrc is sourced, but it probably has a test for interactivity at the top. Things you put before that check should apply, and that's what I do to force PATH when the server doesn't allow user environment or use ~/.pam_environment.
    – muru
    Dec 24, 2016 at 12:41

4 Answers 4

34

You have few possibilities:

  • Set the PATH on the server in ~/.ssh/environment (needs to be enabled by PermitUserEnvironment yes in sshd_config).
  • Use full path to the binary
  • As you mentioned, manually source .bashrc: prefix the command with . ~/.bashrc (or source)

It pretty much depends on the use case, which way you will go.

7
  • 10
    Manually sourcing ~/.bashrc is not necesary, it happens by default. It fact, it seems to me like a bad idea.
    – user232326
    Dec 25, 2016 at 3:53
  • 2
    $PATH does not expand from ~/.ssh/environment, at least not in OpenSSH_7.9p1, OpenSSL 1.1.1b 26 Feb 2019.
    – Jaakko
    Oct 23, 2019 at 8:31
  • 2
    @Jaakko It is probably disabled in sshd_config using PermitUserEnvironment (default in recent versions)
    – Jakuje
    Oct 23, 2019 at 8:35
  • 3
    I figured it out, it was not expanding because I added a line PATH="$PATH:/new/path" to my ~/.ssh/environment when it should have been PATH=$PATH:/new/path
    – Jaakko
    Oct 23, 2019 at 8:42
  • 3
    On MacOS Catalina OpenSSH_8.1p1 be warned you better do $ echo "PATH=$PATH" > ~/.ssh/environment cause $PATH does not expand Oct 21, 2020 at 10:58
9

You are equating local settings to remote settings.

Locally, a bash instance, the present running shell in which you write:

ssh user@host command

Will execute the command ssh (nothing more) as a client ssh.
To do so the local shell needs not to start a sub-shell or a new shell or login.

The command is executed as a ls command is: locally.

It is the client ssh command that opens a network connection to a remote system, where, if correctly authenticated, a new shell will be started to execute the command written as an argument to ssh, or, if no argument is given, expect further commands on that connection.

That new Remote shell necessarily will be a login shell as the remote user (to that system) needs to be authenticated to login. Or, if some specific command is given, just run such command with the authenticated user privileges.

You could see which files are sourced by adding a $file sourced to the start of each file (in the remote system)(root is needed to change /etc/ files):

$ a=(~/.bashrc ~/.profile /etc/bash.bashrc /etc/profile)
$ for f in "${a[@]}"; do sed -i '1 i\echo "'"$f"' was read"\n' "$f"; done

And then just start a ssh console:

$ ssh sorontar@localhost
/etc/profile was read
/etc/bash.bashrc was read
/home/sorontar/.profile was read
/home/sorontar/.bashrc was read

In this case, both bashrc files were read because each profile file had commands to include them, not because the login shell directly sourced them.

$ ssh sorontar@localhost :
/etc/bash.bashrc was read
/home/sorontar/.bashrc was read

In this system, where bashrc is read in both cases.

No need to add a source ~/.bashrc to the command to execute.

Change PATH

All you need to do is include the correct settings to change the "$PATH", either in /etc/bash.bashrc for all users that start a shell in this system. Or in ~/.bashrc for each user that needs it. You could add (or edit) an skeleton of an user .bashrc to /etc/skel/ to have any new user created have the correct file available.

The above is valid only for bash. If you need the setting to work for all shells, probably setting the environment variable PATH using the ssh file ~/.ssh/environment for each user that need it. Or use /etc/ssh/sshrc for a global setting in the system where the ssh server is running (please read the Files section in man sshd for some additional detail).

2

It is possible to execute commands (and set PATH or other environmental variables) in ~/.ssh/rc file. From man ssh:

~/.ssh/rc

Commands in this file are executed by ssh when the user logs in, just before the user's shell (or command) is started. See the sshd(8) manual page for more information.

sshd manual specifies this in its complete Login process description:

  1. If ~/.ssh/rc exists, runs it; else if /etc/ssh/sshrc exists, runs it; otherwise runs xauth. The ''rc'' files are given the X11 authentication protocol and cookie in standard input. See SSHRC, below.

The primary purpose of this file is to run any initialization routines which may be needed before the user's home directory becomes accessible; AFS is a particular example of such an environment.

This is from 2013 BSD manual. From my newer 2020 BSD manual for Arch Linux, these details are given:

  1. If ~/.ssh/rc exists and the sshd_config(5) PermitUserRC option is set, runs it; else if /etc/ssh/sshrc exists, runs it; otherwise runs xauth. The “rc” files are given the X11 authentication protocol and cookie in standard input. See SSHRC, below.

Probably because of this setting ~/.ssh/rc didn't work for me.

I couldn't find at what step sshd runs user's login scripts. Maybe it is set in ssh configuration scripts (in this case it is distribution-dependent).

4
  • 1
    A good etiquette rule on StackExchange is to give a reason while down-voting. I looked through the question and other answers, and see no particular problem with mine. I don't think that my answer is better than others, but it has two good points: a) it is original, it proposes a new distinct solution b) it references the sources of information. If the answer is any wrong, feel free to correct that. Jan 22, 2022 at 15:52
  • No idea why this was ever downvoted. This is a great way to set the PATH. It works both in FreeBSD and Linux (although executed twice in Linux, no idea why). Unlike ~/.profile it's executed for both interactive and non-interactive shells. Unlike ~/.ssh/environment you can use commands and variables like any shell script. Unlike BASH_ENV it's executed once (or twice) instead of sourced at the start of every shell script. Perfect.
    – Daniel
    May 27, 2022 at 18:11
  • 3
    oops, I spoke too soon. ~/.ssh/rc is executed, not sourced. So you can run some commands but they will not change the env. You can't set the PATH with this.
    – Daniel
    May 27, 2022 at 21:11
  • @Daniel - many thanks for your remark! Today I don't know whether that worked for me that time and why I posted that. Maybe just read the manuals? Or could it be sourced on some environments? Maybe I shall leave this answer at the moment and upvote your comment. May 29, 2022 at 8:23
2

If you want one-shot behavior and your SHELL in the remote host is bash you can directly do:

ssh user@host "PATH=/foo/bar command"

If the SHELL is not bash, you can do

ssh user@host "bash -c 'PATH=/foo/bar command'"

You must log in to answer this question.

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