Why Bash unable to find command even if $PATH is specified properly?

28,277

Solution 1

tl;dr

Running ssh 127.0.0.1 ydisplay sources ~/.bashrc rather than /etc/profile. Change your path in ~/.bashrc instead.

details

The only time /etc/profile is read is when your shell is a "login shell".

From the Bash Reference Manual:

When bash is invoked as a login shell, ... it first reads and executes commands from the file /etc/profile

But when you run ssh 127.0.0.1 ydisplay, bash is not started as a login shell. Yet it does read a different startup file. The Bash Reference Manual says:

when ... executed by ... sshd. ... it reads and executes commands from ~/.bashrc

So you should put your PATH settings in ~/.bashrc.

On most systems, ~/.bash_profile sources ~/.bashrc, so you can put your settings only in ~/.bashrc rather than putting them in both files.

There's no standard way to change the setting for all users, but most systems have a /etc/bashrc, /etc/bash.bashrc, or similar.

Failing that, set up pam_env and put the PATH setting in /etc/environment.

See also:

Solution 2

Historically, the profile files (/etc/profile and ~/.profile) were invoked when you logged in (on the text console, what else?) and served many purposess:

  • Set environment variables and other parameters (e.g. umask) for the session.
  • Run extra programs at the start of the session (e.g. email notification).
  • Run the program for the session, if different from the shell (e.g. another shell or X Window).
  • Set terminal parameters (e.g. stty).
  • Set shell parameters (e.g. aliases).

All these purposes weren't identified as separate until later. Because the profile scripts may do things that only make sense in an interactive session (terminal interaction, start other programs), when remote shell invocation (rsh) was introduced, the makes of rsh decided not to invoke the remote shell as a login shell, so that the profile scripts aren't executed. (Some versions of rshd have an option to run the remote shell as a login shell.) Ssh copied this behavior in order to be a drop-in replacement for rsh.

If you want to have your profile scripts executed, you can invoke them explicitly.

ssh 127.0.0.1 '. /etc/profile; . ~/.profile; ydisplay'

Note the command . to load the profile scripts inside the shell: they are commands to be executed inside that shell, not an external program.

If you want to set an environment variable globally for all users, there is another method on many systems: instead of defining it in /etc/profile, define it in /etc/environment. This file is read through the pam_env module; most Linux distributions are set up to read it.

If your login shell is bash, there is a further possibility. Normally, you should not set environment variables in .bashrc (because they won't be set in X sessions except if you go through a terminal with an interactive shell, because they won't be set if you log in interactively on a text console or over ssh, because they'll override custom settings if you invoke a shell inside another program). However, bash has a strange feature that I've never understood: it reads ~/.bashrc in two unrelated circumstances:

  • in interactive shells that are not login shells;
  • in non-interactive shells that are not login shells, if bash thinks it has been invoked by rshd or sshd.

When you run a command over ssh, you are in the second case. You can arrange to have your profile read by reading /etc/profile and .profile from .bashrc. Include the following code in your ~/.bashrc:

case $- in
  *i*) :;; # this is an interactive shell, fine
  *) # This is not an interactive shell! This must be a non-interactive remote shell session.
    . /etc/profile; . ~/.profile
    return;;
esac
Share:
28,277

Related videos on Youtube

SIGSEGV
Author by

SIGSEGV

Updated on September 18, 2022

Comments

  • SIGSEGV
    SIGSEGV over 1 year

    I am specifying path to my command in the file /etc/profile:

    export PATH=$PATH:/usr/app/cpn/bin
    

    My command is located in:

    $ which ydisplay 
    /usr/app/cpn/bin/ydisplay
    

    So, when I performing "echo $PATH" output is looks like:

    $ echo $PATH
    ...:/usr/app/cpn/bin
    

    And everything is OK, but when I am trying to launch my command via SSH I am getting error:

    $ ssh 127.0.0.1 ydisplay
    $ bash: ydisplay: command not found
    

    But the my path is still present:

    $ ssh 127.0.0.1 echo $PATH
    ...:/usr/app/cpn/bin
    

    Please explain me why Bash unable to find ydisplay during SSH session and how to properly configurate SSH to avoid this issue.

    More over, if I specifying $PATH in local file .bashrc in the current user all works correctly. But I want to modify only one file instead specifying a lot of files for each user. This is why I am asking.

    • Bananguin
      Bananguin over 11 years
      does just running ydisplay work? does ssh 127.0.0.1 /usr/app/cpn/bin/ydisplay work?
    • mnmnc
      mnmnc over 11 years
      When you are not logged (you do not have a remote session) but only sending a command remotely you dont have access to environment variables the same way because your .bashrc / .profile files are not executed. This is the reason as they are responsible for setting variables for current session.
    • SIGSEGV
      SIGSEGV over 11 years
      @mnmnc, you said that .bashrc is not executed, but if I specifying $PATH in this file all works fine. How can it happen?
    • Ulrich Schwarz
      Ulrich Schwarz over 11 years
      Just a side note: ssh 127.0.0.1 echo $PATH does not do what you might think it does: the shell expands $PATH before ssh is even executed, so that doesn't prove or disprove anything.
    • bsd
      bsd over 11 years
      this stackoverflow question might be of some help
    • Mikel
      Mikel over 11 years
      @SIGSEGV Actually, ~/.bashrc is sourced. This fact is hidden futher down in the man page (and in Bash Reference Manual - Invoking Bash - Startup Files - Invoked by remote shell daemon).