Where should we put the statement to initialize our PATH, in ~/.bash_profile
or ~/.bashrc, and why?
Where to place statements such as export PATH=~cs3214/bin:$PATH is a recurring
topic that is the subject of much confusion on the Internet.
Fundamentally, since environment variables are per-process properties that are inherited from a parent process such as a shell, they need to be initialized every time you log into a system and start new processes. The same goes for shell settings such as aliases, etc. To perform this initialization, the shell will read a set of startup files when it starts.
There is one problem, however: some of these statements are not
idempotent, that is, executing them more than once changes their
outcome. For instance, export PATH=~cs3214/bin:$PATH, if run twice,
would result in a PATH that starts with:
/home/courses/cs3214/bin:/home/courses/cs3214/bin:.... because the
directory was prepended twice.
To deal with this situation, the shell adjusts which startup files it reads depending on when and how it is invoked. This process is described in Section 6.2 of the bash manual.
If a user first logs onto a machine, the first shell
that is started is a so-called "login" shell. A "login" shell is
not a different kind of shell, it is simply a certain way of starting
bash that conveys to bash that it should act in "login" shell mode.
Specifically, bash will do so when the first character in its av[0]
is a hyphen -.
PID TTY STAT TIME COMMAND
1337700 ? S 0:00 sshd: gback@pts/2
1337701 pts/2 Ss 0:00 \_ -bash
(Side note: now you understand why the exec* family
of system calls distinguishes between the name of an executable and
separately allows the caller to specify av[0] - the two don't need
to be identical!)
As a login shell, bash will read ~/.bash_profile (see the
bash man page for more details.) This is where you would put non-idempotent
initialization such as prepending/appending to the PATH.
Once the user is logged on, they may start additional shells.
Try typing bash, for instance. These shells (whose ancestor is a
login shell) will inherit the PATH settings since they are part of
the environment. However, they will not automatically have such settings
as aliases which are needed by the user.
Such interactive, non-login shells read the file ~/.bashrc and interpret
the settings in it. To make sure that the first interactive, login shell
gain the same settings, the bash manual recommends "sourcing"
~/.bashrc from it. That is, your ~/.bash_profile file should
contain
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
The source command in bash opens a script file
and reads and interprets the commands in it in the context of the
current shell (it does not start a new shell process because then
the effect of these commands would be lost when that process exits.)
An alias for the source command is, btw, a single dot .
So if you know that your .bashrc exists you could replace the above
with
source ~/.bashrc
The bash shell (and other shells) not only execute in interactive
mode where the receive input from users, but they are also used as
a command interpreter to interpret shell scripts. Shell scripts are
text files containing shell commands. When a new shell is started
to interpret a shell script, the shell is said to be non-interactive.
Non-interactive shells do not read either ~/.bash_profile or
~/.bashrc on startup. Note that starting such a shell is
different from using the source command.
Shell scripts use a #!/bin/bash or #!/bin/sh
shebang at the beginning of the file.
/bin/sh is btw a symbolic link to /bin/bash on most modern
Linux machines. In other words, running /bin/sh will run
/bin/bash but tell it to operate in compatibility mode with
the original Unix shell that was called /bin/sh, the so-called
Bourne shell.
In addition to your personal startup files, there exist system-wide
startup files which the shell also reads.
These startup files may provide good default settings in case
users don't create their own startup files (on some systems),
and they may also help set up the environment so that users
can conveniently use the software installed on the machine.
See ls /etc/profile.d/* for examples.
How do I recover if I screwed up my bash files?§
Run these commands on your local machine:
ssh <user>@rlogin.cs.vt.edu mv .bashrc .bashrc.outoftheway
ssh <user>@rlogin.cs.vt.edu mv .bash_profile .bash_profile.outoftheway
This will run these commands directly from sshd, bypassing your
non-functioning shell.
Alternatively, you can run
ssh <user>@rlogin.cs.vt.edu bash --noprofile --norc
# and then type here shell commands - no prompt will display
pwd
mv .bashrc .bashrc.outoftheway
mv .bash_profile .bash_profile.outoftheway
<Ctrl-D>
This will start a shell, but prevent the shell from executing either .bash_profile
or .bashrc.
What's the difference between .profile and .bash_profile?§
The name .profile was used by the original Bourne shell, but is supported by
bash as well as a fallback. As described in the bash man page (section INVOCATION):
"...it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order,
and reads and executes commands from the first one that exists and is readable.
This means that .profile is not consulted if .bash_profile exists.
When do changes to these files take effect?§
As discussed above, these files are consulted when a shell process starts up. This means that in order to properly test changes to these files, you need to log off and then log back on so that you obtain a login shell (and subsequent) shells, which then goes through the process of reading those files upon startup.
To activate any changes, you can also (as a temporary fix) ask the current shell
to reread a file in question using the source command. However, this will
(a) not provide a true test since the commands are applied on top of the environment
of the current shell and thus (b) not work well for non-idempotent commands (PATH
entries may be duplicated, etc.)
When using vscode, certain files are read only when the vscode server first starts. In this case, logging off and on will not reread those files as long as the server isn't restarted. You may need to restart the vscode server. To that end, vscode provides an option: Remote-SSH: kill VS Code Server on Host.