Shell Config Ergonomics

Loren (mcint) January 29, 2023 [Terminal] #cli #bash #config
# ~/.profile
  arg="$(realpath "$arg")"
  if [ -f "$arg" ]; then
    . "$arg"
  elif [ -d "$arg" ]; then
    debug "${FUNCNAME[0]}: no file '$1'"

src "$HOME/.cargo/env"
for f in ~/.shell/*; do
  src "$f";
# .shell/paths
# .shell/aliases
# .shell/funcs
#src /usr/local/etc/profile.d/ # replaced by z.lua

src ~/.env
src ~/.config/shell/fzf/key-bindings.$_shell
src ~/.config/shell/fzf/completion.$_shell

dedup_path() { export PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"; }

[[ "$GVM_ROOT" ]] || { [[ -s "/Users/mcint/.gvm/scripts/gvm" ]] && source "/Users/mcint/.gvm/scripts/gvm"; }

I have a src script, so I don't have to rewrite the [[ -f $file ]] && $file idiom every time. It also reports or logs errors for files that aren't present but are called.

A paths file, with helper functions to read--newline delimited--, prepend, and append.

As I build more tools from C* ecosystems, I recently wrote a tool to prepend and append to other paths, package conf, libraries, and man pages.


Aliases are invaluable as a quick shorthand. I write aliases often within the first few minutes of using a host alias p=pushd pp=popd d="dirs -v". Using the (bash) directory stack helps me externalize memory, so current status doesn't have to stay in my head. It stays with project context on the machine, even if I walk away. Unfortunately bash doesn't offer a way to export it, and scripting gdb to inspect it and save it out is not possible on macOS, to my dismay, so it can't be preseved by tmux-resurrect across restarts, although window names, pane PWD, and nvim open files are preserved.

Aliases for inspection also make it easier to understand the environment exertion. alias tt=type.


Shell functions are essential shorthand if you have to pass parameters in midway through a pipeline construct. I also maintain functions to save select (or all) aliases, save bash functions by name, and to edit functions and reload them.


Path manipulation is a commond way to manipulate a shell environment. It's so common and powerful that many tools are build around doing just that.

Aliases alias ppath=<<<"$PATH" tr : '\n' alias pathgrep='<<<$PATH tr : "\n" | xargs fd . | grep'

Functions path_prepend, path_append, and path_remove

virtualenv - install project-local copy of packages direnv - (allows much more, but is often first pitched to) automatically update your environment

Ensemble configuration

In the wild / already in your life / already familiar

Updating configuration with directories, examples that prove the utility and strength of the pattern:

My use in shell configuation

I move disabled shell files into ~/.config/shell/disabled, and don't handle directories in my bulk-sourcing script.

I have, on 2 or 3 occasions, written tracing scripts into my shell configuration to debug long initializion times. I'll start thinking seriously about it at .5 seconds, and then, when my computer is under load at that jumps over 3 seconds, I'll bite the bullet and dig into my shell rc files to cut out the inconsiderate loading scripts.


I don't want to mix many separate concerns in the same file, and I enjoy quick cycle times where I can Save-Load-Test configuration files or Save-Spawn-Verify desired configuration in new shells.

Other power tools

A friend at the OCF wrote how, to inspect scripts and quickly learn about the system.

I've attend -e --edit flags to edit scripts, and more recently -c --create flags, to begin new utility shell scripts in single command.

the other profile

.bash_profile consists of

. .profile
# prompt set up, split over two dozen lines for editability
# I like venv default modifications so dislike forced resetting of prompt

# Return codes from a previous command can be included in a static definition of PS1,
# including conditional styling. PROMPT_DIRTRIM handles the other major concern
# Variables to include local context sometimes make an appearance. Serving like a 
# window title, or a short todo list, in one of the panes of dozens of local tmux
# windows, or on remote servers.

[[ -r "/usr/local/etc/profile.d/" ]] && . "/usr/local/etc/profile.d/"
eval "$(lua /usr/local/share/z.lua/z.lua --init bash enhanced once echo)"

# in: ~/.env, and ~/.paths ##export GOPATH="$HOME/go"; export GOROOT="$HOME/.go"; export PATH="$GOPATH/bin:$PATH"; # g-install: do NOT edit, see

_conda_load(){ [[ -f ~/.bash_conda ]] && . ~/.bash_conda; }
_mamba_load(){ [[ -f ~/.bash_mamba ]] && . ~/.bash_mamba; }

I defer conda loading because I don't want to be in that environment by default, and becuase it adds a noticable (but not always unbearable) delay to shell startup times.

Back to top