An article like this would be better if it didn't gloss over the different bash startup files and when they get loaded.
Many times when I help someone debug their shell, the root cause is not understanding what each of those files is for.
Adding an echo to the wrong one and not having it check if the shell is interactive can break all sorts of things like scp for example. And in weird and non-obvious ways.
It's outright wrong since it forgets ~/.bash_login
The startup files are mostly comprehensible if you understand the following two oddities:
* Some pseudo-terminal programs always start the shell as interactive; other pseudo-terminal programs always start the shell as non-interactive.
* Shells started from SSH are different than all other shells, regardless of interactivity.
* Shells other than bash are broken-by-design and really should not be used if you have any choice, but in the real world you do have to know at least a little about how to deal with their brokenness.
The shell man pages don’t really help either. They use terms like “login shell” and “interactive shell” without really defining or differentiating them.
They're defined, it just doesn't go into the weird details. From the man page:
A login shell is one whose first character of argument zero is a -, or
one started with the --login option.
(not mentioned: the login(1) program does the dash thing when you log into a VT (as opposed to graphically, which has a whole nother complicated dance); login(1) is traditionally spawned by getty(8) which is in turn spawned by init(8). Some terminal emulators aggressively spawn login sessions; this is wrong but a common workaround for people who failed to configure their shell correctly, or who aren't using bash and thus don't have all the configurability)
An interactive shell is one started without non-option arguments (un‐
less -s is specified) and without the -c option, whose standard input
and error are both connected to terminals (as determined by isatty(3)),
or one started with the -i option. PS1 is set and $- includes i if
bash is interactive, allowing a shell script or a startup file to test
this state.
(or in other words: an interactive shell is one that outputs a prompt and waits for you to input something, as opposed to shells that are used to execute a file/pipe/string)
Further down, and often forgotten:
Bash attempts to determine when it is being run with its standard input
connected to a network connection, as when executed by the historical
remote shell daemon, usually rshd, or the secure shell daemon sshd.
(this goes on to explain how .bashrc is loaded even when it normally wouldn't be)
These are orthogonal, and can be combined in various ways (there are also a few other possibilities mentioned in this part of the man page, but they're much less relevant):
Login shells are normally interactive, whether started sanely by login(1) or by unreasonable terminals.
Non-interactive login shells are rare and weird and sometimes declared "unsupported" by config-file authors, but do work if you're careful (a common error is to do a pseudo-motd in /etc/profile, which will break for non-interactive logins if you don't explicitly check for them; these days, a lot of things should really be done by PAM instead for better reliability).
Interactive non-login shells are what you normally create unless your terminal is dumb, or what you always create if you start bash within bash.
Non-interactive non-login shells are what scripts use.
The SSH special logic is ignored for the common case where you're doing an interactive login shell. But if you are doing a non-interactive non-login shell, it gives you chance to fix your PATHs, since no other startup files have been read. I think those are the only two possibilities with a normal SSH configuration (and in unusual configurations you are probably being restrictive); other shells would be created as a child of the initial shell. (Remember that SSH feeds its arguments to the shell directly; it does not quote them sanely!)
When I was going back and forth between bash and zsh more, I started keeping a ~/.commonrc file that uses only some subset of syntax that's compatible with both, and sourcing that file from both .bashrc + .zshrc .
Things like adding to the PATH, aliases (well, ones that work in both bash and zsh), shell functions, etc, can just go in .commonrc and be forgotten. It's nice.
Step 1: echo $SHELL
Step 2: should mention profiles
Step 3: is likely confused by terminal emulators that do not start login shells so profile is not reread (this is also why you don’t have dozens of duplicate path entries all the time). Trial and error is misleading without knowing why login items are or are not reread.
Step 4: adding these statements to rc’s will generally lead to duplicate entries for nested shells
Step 5: starts nested shells, so prior instances of step 4 demonstrate the problem with step 4. A login shell can generally be started cleanly like exec env -I $SHELL -l
Problem 2: comes from an all too common choice not to understand profiles and login shells. This includes vscodes choices.
Problem X: adding empty string to your path. This has all kinds of bad outcomes.
There's room for an equally incredibly helpful illustration of how over complicated with edge cases it can be to add directories to your Windows PATH (and set initial shell start commands).
I deal with both command.com and bash on a regular basis and both have odd scope gotcha's .. Windows has the Admin | current user split and options for affecting session shells or all future shells, and two(?) potential registry entries and|or environment strings.
systemd has `/etc/environment.d/`[1], which can be used to set/update `PATH`. Support for getting that into a users session isn't very wide (currently GDM and KDE plasma only[2]), but hopefully will be in the future.
There are a couple of problems with environment.d - it cannot be made idempotent, and can't splice into the middle.
These shouldn't be a problem since it runs early enough and should be controlled by a single sane party, but makes porting a pain.
I don't understand what problem your [2] link is referring to, since the whole point of systemd user sessions is that it happens before graphical stuff.
I've used a Mac and linux for 20+ years and never knew about this. Maybe coming to OS X from linux meant I never bothered to ask the official way to set a path.
How did you find out about it? This feels like a hidden secret.
I don't think I've ever lost a setting from there. the manpage (path_helper) was last updated 2007, so I think it's fair to say it's not a very dynamic corner of the OS.
Ah yes, an article which is both incredibly helpful and illustrates the absurdity of how over complicated it is to do something simple like adding a directory to your PATH.
Many times when I help someone debug their shell, the root cause is not understanding what each of those files is for.
Adding an echo to the wrong one and not having it check if the shell is interactive can break all sorts of things like scp for example. And in weird and non-obvious ways.