Effectively Shell-ing: Decouple your environment variables from specific shells by sourcing .profile everywhere

Since this is about the shell, I’m going to make a plug for my friend Dave Kerr’s amazing ebook: Effective Shell. If you’re going to muck about in terminals/shells, you really ought to go read it.

….

No. Really. I’ll wait. You’d be stupid not to. Go read it, or at least start reading it, then come back.

OK. Now that you’re a shell ninja, and know how some strong shell-fu can make an engineer more powerful… let’s talk variables and imports.

If you’ve ever so much as setup a dev environment and installed some non-trivial tools, you’ve likely encountered installation guidance like this (example from reactnative.dev, as of 08.22):

Note how they’re already qualifying the fact that you might have to put these exports in one file if you use bash, or in another file if you use Zsh. But they’re not done yet. They go on to clarify that if you’re ever using some shell other than the one you already configured, you’ll then still need to source those variables into your current shell:

So… sure… you could do all that. And, you could wrestle with the fact that other bits of your system (such as XCode) will periodically open up their own choice of shell (often bash), in a non-interactive way, where you can’t even exercise this option. And so everything will break. And you could accept that as inevitable. (Sucker.)

OR, you could take advantage of a simple and inexplicably overlooked option which makes this whole problem go away. In addition to the shell-specific files already mentioned above…

~/.bash_profile
~/.zprofile

…there is by default another, more universal file on POSIX-compliant systems: ~/.profile. (That’s the user-specific version for your own account. In theory, you could also tinker with etc/profile, but as a general rule I prefer to stick to messing about with my user files rather than system-level ones.)

Sure, you could run around manually source-ing some other profile into your current shell… but why would you? Why not go with the even easier and more sustainable option of ensuring that you always have all the same environment variables in all your shells?

First, instead of copying them hither and yon… simply place all needed variables/exports into one place: ~/.profile instead of repeating them across all the bash/zsh-specific versions.

Then, open up your user’s shell-specific configuration files (such as ~/.bashrc and/or ~/.zshrc, and/or ~/bash_profile and/or ~/zprofile… or those for rbash, dash, tmux, etc. ) and add to them all a single simple line:

[[ -s "$HOME/.profile" ]] && source "$HOME/.profile"

# SOURCE the master sh .profile, for concerns shared across all shells, login and non-login

[[ -s "$HOME/.profile" ]] && source "$HOME/.profile" # Load the default .profile

# Everything after this should be specific to your Zsh/bash login shells

That’ll do it. You should now have all the same variables in all your shells, because they’re all source-ing from the same location.

And, if you’d maybe like to embellish that with some explanatory comments so you remember what’s happening here:

# SOURCE the master .profile, for concerns shared across all shells, login and non-login
[[ -s "$HOME/.profile" ]] && source "$HOME/.profile"
# Everything after this should then be specific to your interactive shells

I have also found it useful to provide an indicator so I can tell when these variables have been imported successfully into a given shell, so I include this at the end of ~/.profile:

###### LAST
echo ".profile is sourced"

Once that’s done, I find that my individual shell config files can be very lean and mean, with only shell-specific items. For instance, my ~/.zshrc can exclusively be about Zsh customizations like the amazing oh-my-zsh, which won’t do me any good over in ~/.bashrc. Meanwhile, it’s only in ~/.profile that I need to write universal workflow utility scripts and project aliases such as:

alias rns="cd ~/PROJECTS/react-native-scaffold && nvm use --lts"

Notes:

If you want to be more surgical, it may be useful to understand the difference between –profile files and –rc files, between interactive shells and non-interactive shells, and login vs. non-login shells. Maybe check out this article and/or this one, or go finish reading the related topics in Effective Shell. That said… it’s complicated, especially for Mac users, which is why I’ve been pretty general about which files you might need to import the ~/.profile into. YMMV.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: