Why do 'ssh host echo $PATH' and printing the $PATH after ssh'ing into the machine give different results?

15,940

Solution 1

In the first scenario you're logging into the system so you're executing what's called a login type of connection. A login sources the scripts in $HOME/.bashrc and $HOME/.bash_profile if you're login shell is Bash. These 2 scripts in turn typically source the files /etc/bashrc and the files under /etc/profile.d/*.sh.

For the second scenario you're only sourcing the file $HOME/.bashrc. This type of login is called interactive.

You can read more about these in the INVOCATION section of the bash man page.

To change this behavior you can include the $HOME/.bashrc_profile as part of the interactive execution to get the desired behavior:

$ ssh user@remotehost '. $HOME/.bash_profile; echo $PATH'

-or-

$ ssh user@remotehost 'source $HOME/.bash_profile; echo $PATH'

References

Solution 2

Session initialization files are weird, for hysterical historical reasons.

A long time ago, you logged in on the console, period. When you logged in, you got a shell, and the shell would load some initialization file. For Bourne-style shells, there are two initialization files: /etc/profile (for all users) and ~/.profile (for each user). The initialization file can set environment variables such as PATH, load applications that you want to run in every session (e.g. a mail indicator), etc. If a shell isn't a login shell, it doesn't need to start anything or set environment variables, so there's no need to run any initialization file. Surely there isn't…

Actually, shells gradually gained interactive features, so there's a need for an initialization file for interactive shells. Ksh has ~/.kshrc, bash has ~/.bashrc, zsh has ~/.zshrc.

Remote interactive logins run /etc/profile and ~/.profile. But these are not suitable to non-interactive logins because they may start non-interactive programs. Unfortunately, no standard has emerged for an initialization file for non-interactive logins.

You can run /etc/profile and ~/.profile manually if you're sure they won't produce any output or start any program, just set environment variables.

ssh foo '. /etc/profile; . ~/.profile; somecommand'

If you have parts in your .profile that only make sense in an interactive session, you can put them inside a conditional instruction.

## (in ~/.profile)
case $- in
  *i*) # the shell is interactive
    newmail;;
esac

If your login shell is bash, you can take advantage of a quirk: the initialization file ~/.bashrc is read by interactive shells, and also by non-interactive shells whose parent process is called rshd or sshd. Other shells don't have this quirk.

## (in ~/.bashrc)
case $- in
  *i*) # interactive shell: set prompt, completion settings, key bindings, etc.
    …;;
  *) # non-interactive shell: our parent must be rshd or sshd
    . ~/.profile;;
esac
Share:
15,940
Programming Noob
Author by

Programming Noob

Updated on September 18, 2022

Comments

  • Programming Noob
    Programming Noob over 1 year

    On a particular host, when I ssh into the machine and enter echo $PATH I get

    /home/wxy/bin64:/home/wxy/bin:/usr/kerberos/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/bin:/usr/bin
    

    and if I execute ssh host 'echo $PATH' I see this:

    /usr/local/bin:/bin:/usr/bin
    

    Why is this discrepancy?

    Also what can I do if I wanted to see the former result using the latter command?

  • Programming Noob
    Programming Noob almost 11 years
    And is there any way I can have the sourcing from the login type while having an interactive connection?
  • slm
    slm almost 11 years
    @ProgrammingNoob - see updates.
  • prayagupa
    prayagupa almost 7 years
    /etc/bashrc is was very useful to me as it loads environment vars (no need to source ~/.bash_profile) which need to be visible to some scripts I needed to run via ssh.