-
Notifications
You must be signed in to change notification settings - Fork 0
/
bash_startup_files.txt
139 lines (117 loc) · 7.03 KB
/
bash_startup_files.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
- This document explains how Bash loads files upon starting up *on MacOS*,
which affect its initialization. This is helpful to understand when we want
to make permanent changes to these initializaton settings, e.g. we want a
particular item to be in our PATH every time we fire up Bash. It's also
helpful if we're looking to clean up our PATH - if some application has
polluted it, we can use this knowledge to figure out how that application
injected items onto our $PATH, and thus how to reverse the mess.
- Concept: interactive vs non-interactive invocation.
Bash can be invoked as an interactive shell (i.e. where you have a
bleeping cursor, waiting for you to input commands...) or as a
non-interactive shell (e.g. executing 'bash <file>' is a non-interactive
invocation).
- Concept: login vs non-login shell.
I'm not entirely clear on this distinction, but what's important to know is
that (by default on Mac) interactive shells are treated as login shells, when
launched using the default Terminal (the same applies to iTerm).
This is not the case for Linux and other Unix-like systems, which run
interactive, non-login shells by default.
Here's an example of a non-login shell: A shell process launched from
another shell process, e.g. typing 'bash' from an interactive bash session.
Because of this behaviour (and because I use MacOS), we'll first cover how
Bash starts up for interactive login shells.
Note: It is possible to check whether a shell is a login shell by running
'shopt login_shell'.
- When invoked as an interactive login shell, or as a non-interactive shell with
the --login option:
1. Bash reads and executes the /etc/profile file, if it exists.
2. Then, Bash looks for ~/.bash_profile, ~/.bash_login, and ~/.profile,
in that order. The first of these files that is found is then executed;
the remaining are ignored.
- Think of /etc/profile as doing a 'global' Bash initialization and the ~/*
files as doing some initialization work specific to the current user.
- Don't modify /etc/profile (for one, it's a system file, so not meant to
be modified; you'll need sudo!). Instead, if wanting to set global
initialisation settings, let's first understand what's in /etc/profile:
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
The first if condition tests the existence of the
/usr/libexec/path_helper executable and executes it if it does, and the
second if condition tests if the BASH variable is set (in a Bash session,
this'll be set to the path to the Bash executable, /usr/bin/bash), executing
the /etc/bashrc file if it exists and is readable.
Let's break each of these down:
1. The path_helper utility reads the files /etc/paths and /etc/manpaths,
adding their contents to the PATH and MANPATH environment variables,
respectively. Next, it searches for files in the directories /etc/paths.d
and /etc/manpaths.d, appending their contents to the PATH and MANPATH
environment variables. Specifically, the lines in these files are treated
as individual path elements to be added to (MAN)PATH.
So, to add stuff to PATH or MANPATH that should be reflected globally,
simply head over to /etc/(man)paths.d and create a new file containing
the path elements you want in separate lines. Don't modify /etc/(man)path
since these are read-only.
2. The /etc/bashrc file is another file that should NOT be modified. Instead,
let's understand what's in it:
# System-wide .bashrc file for interactive bash(1) shells.
if [ -z "$PS1" ]; then
return
fi
PS1='\h:\W \u\$ '
# Make bash check its window size after a process completes
shopt -s checkwinsize
[ -r "/etc/bashrc_$TERM_PROGRAM" ] && . "/etc/bashrc_$TERM_PROGRAM"
Not sure what PS1 is, but I believe the if condition usually fails upon
startup.
So, what we end up having is that the /etc/bashrc_$TERM_PROGRAM file is
executed, if it exists and is readable. The TERM_PROGRAM variable is set to
whatever the terminal (emulator) is, e.g. it's "iTerm.app" in iTerminal and
"Apple_Terminal" in the default MacOS Terminal.
Don't edit the /etc/bashrc_$TERM_PROGRAM files. I think they're there for
terminal emulator-specific set-up details - NOT for us to throw in any
customisation.
- Best Practice for modifying the initialization of interactive (login) Bash
shells:
To add stuff to the (MAN)PATH globally, head over to /etc/(man)paths and add
a text file with each line containing an individual path element you'd like
to add. Alternatively, see the last tip.
DON'T edit /etc/profile or /etc/bashrc. I don't know if
/etc/bashrc_$TERM_PROGRAM is safe to edit (nor if you'd even want to edit it,
since surely you'd want your Bash initializations to apply to Bash in any
terminal (emulator), not just a specific one). Thus, when it comes to global
initializations, don't do anything complex: Just add stuff to (MAN)PATH as
described.
Any initialization stuff that's more complicated than adding to (MAN)PATH:
head over to ~/.bash_profile. Of course, these initializations will be local
to your particular user, but if you're the only user of the system then this
is OK.
- When invoked as an interactive non-login shell, Bash reads and executes ~/.bashrc, if it exists.
According to the GNU manual (see reference 3)), typically the ~/.bash_profile
sources ~/.bashrc (if it exists), either at the end or the beginning of the
file, after or before any login-specific initializations. The MacOS default
~/.bash_profile doesn't do this - I'm guessing because interactive non-login
shells aren't the default - but it might be a good idea to do the following:
1. Make ~/.bash_profile source ~/.bashrc (if it exists, i.e. using the line
'if [ -f ~/.bashrc ]; then . ~/.bashrc; fi').
2. Place initialization code in ~/.bashrc rather than ~/.bash_profile.
This ensures consistency with other Unix-like systems (which tend to place
initialization settings in ~/.bashrc since interactive non-login shells are
the default).
- When invoked non-interactively, Bash looks for the variable BASH_ENV in the
environment, using its value as the name of a file to read and execute (but
note that the value of PATH is not used to search for the filename).
On my Mac, BASH_ENV is not set to anything by default.
Placing the single line 'env' in a file 'script' and executing 'bash script'
at the command-line, we see that, indeed, a non-interactive shell has a very
minimal initialisation. I see just 5 or so environment variables, all of
which are set by the Terminal.
Reminder: A non-interactive shell can be initialized in the same way as an
interactive login shell by passing the --login option.
- References::
1) https://apple.stackexchange.com/questions/1486/where-are-bash-profile-startup-files
2) http://www.softec.lu/site/DevelopersCorner/MasteringThePathHelper
3) https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html