Shell Environment - uhop/dotfiles GitHub Wiki
Executed for every bash session (interactive and non-interactive). The file is organized into two phases: environment setup (always) and interactive setup (gated).
The internal variable __INTERACTIVE (yes or empty) gates interactive-only setup. Non-interactive shells (e.g., ssh host some-command) get PATH, exports, and aliases but skip completions, tool initialization, keybindings, and fastfetch. VISUAL is exported as micro independently.
- Color prompt enabled for
xterm-color,xterm-256color,screen-256color - Uses bash-git-prompt (via brew) with
Single_linetheme - xterm title set to
user@host: dir
Brew shell environment is initialized early. On Linux it uses the standard /home/linuxbrew/.linuxbrew path; on macOS it checks /opt/homebrew (Apple Silicon) then /usr/local (Intel).
| Path | Purpose |
|---|---|
~/bin |
User scripts |
~/.local/bin |
Custom CLI utilities |
$(brew --prefix)/opt/util-linux/bin |
util-linux tools |
$(brew --prefix)/opt/util-linux/sbin |
util-linux admin tools |
$PYENV_ROOT/bin, $PYENV_ROOT/shims
|
pyenv |
$BUN_INSTALL/bin |
Bun runtime |
~/.lmstudio/bin |
LM Studio |
PATH is deduplicated at the end of .bashrc using a bash function _dedup_path() that also ensures the nvm path comes first.
On Linux, ssh-agent runs as a systemd user service at ~/.config/systemd/user/ssh-agent.service. The service binds the well-known path %t/ssh-agent.socket (%t = $XDG_RUNTIME_DIR) via ExecStart=/usr/bin/ssh-agent -D -a %t/ssh-agent.socket and publishes SSH_AUTH_SOCK into the systemd user environment via ExecStartPost=systemctl --user set-environment.
ssh-agent has no LISTEN_FDS support, so a separate .socket unit would be a dead listener on OpenSSH <10 โ every client would hang. (OpenSSH 10.0p2+ added a fallback that reads $SSH_AUTH_SOCK from the service's environment, but the explicit -a works on every version.) The service also overrides distro-shipped /usr/lib/systemd/user/ssh-agent.{service,socket} โ notably Ubuntu 25.10's units listening at %t/openssh_agent โ unifying the whole fleet on one socket path.
For shells that don't inherit the systemd user environment (e.g. ssh host bash non-interactive), .bashrc defensively exports SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/ssh-agent.socket when XDG_RUNTIME_DIR is set.
One agent per user login, shared by every shell. No eval "$(ssh-agent)" in .bashrc, no orphaned agent processes. ~/.ssh/config sets AddKeysToAgent yes globally, so the first time ssh uses a key it's auto-added to the agent โ no explicit ssh-add needed. Keys persist until logout or systemctl --user restart ssh-agent.
On macOS the Keychain-integrated launchd agent handles this โ the systemd unit is excluded via .chezmoiignore on non-Linux. On Linux hosts without a user systemd manager (rare: bare containers), the installer script skips gracefully and SSH still works manually.
See Setting Up a New Machine ยง After setup: what's running for the full list of services.
| Tool | What it does |
|---|---|
| bash-completion | Loaded from brew prefix |
| git-completion | Loaded from ~/.config/git/git-completion.bash
|
| nvm | Node Version Manager โ sets NVM_DIR, sources nvm and its completions |
| pyenv | Python version manager |
| bun | JavaScript runtime |
| doas | Completion for doas command |
| zoxide |
z command (cd replacement) via zoxide init bash
|
| broot | File manager launcher br
|
| fzf | Fuzzy finder โ sets up keybindings and custom completions (see below) |
| playbash | Bash completion for the playbash runner |
| pet | Snippet manager โ ppet, Ctrl+x Ctrl+r search, Ctrl+n next parameter |
| iterm2 | iTerm2 shell integration (if available) |
fzf is a general-purpose fuzzy finder, wired into bash so that common operations become a fuzzy-pick instead of a typed path or a half-remembered command.
Default key bindings (shipped by fzf's key-bindings.bash):
| Binding | What it does |
|---|---|
| Ctrl+R | Fuzzy-search shell history. Replaces the default reverse-i-search with a full fuzzy picker โ type any fragment that appeared anywhere in a previous command line, live-preview matches, and press Enter to drop it at the prompt. The single biggest daily-use win from fzf. |
| Ctrl+T | Paste a fuzzy-picked file path onto the current command line |
| Alt+C |
cd into a fuzzy-picked subdirectory |
Beyond the key bindings, fzf powers:
-
Per-command completions for directory/path/host arguments on common commands (see fzf completions below) โ trigger with Tab after the trigger sequence
**. -
fzf-git.sh key bindings (branches, tags, remotes, stashes, PRs) if the script is present at
~/.local/vendors/fzf-git.sh. - pick โ the interactive command reference uses fzf to browse and execute curated commands by topic.
- pet โ snippet manager search (Ctrl+x Ctrl+r).
When fzf is available, custom completions are registered:
| Completion type | Commands |
|---|---|
| Directory |
z, tree, ltr, zl, zpush, zp
|
| Path |
cp, mv, rm, cat, l, ls, ll, la, lla, l., mic, micro, gimme, rcp, rmv, rup, rsy
|
| Host |
ping, prettyping, sftp, ssht, mosh, mosht
|
Also sources fzf-git.sh from ~/.local/vendors/ if available.
| Variable | Value | Purpose |
|---|---|---|
EDITOR |
micro |
Default editor |
VISUAL |
micro |
Visual editor |
BASH_SILENCE_DEPRECATION_WARNING |
1 |
Suppress macOS zsh warning |
FORCE_COLOR |
3 |
Force 256-color support in tools |
GITHUB_USERNAME |
(rendered by chezmoi from the githubUsername data value) |
Used by utilities and as the default basis for JOT_AWS_S3_BUCKET. A value in ~/.env overrides the chezmoi-rendered default. See GITHUB_USERNAME below. |
JOT_AWS_S3_BUCKET |
${GITHUB_USERNAME}-notes |
S3 bucket for jot. Exported only if not already set and GITHUB_USERNAME is non-empty โ so ~/.env can pin a custom bucket, and users without a GitHub identity don't get a bogus -notes value. |
On interactive terminals (not inside Windsurf), fastfetch is shown at shell level 1 (direct terminal) or level 2 inside tmux.
If ~/.env exists, its contents are exported via set -a / . ~/.env / set +a.
Shape. ~/.env is a plain KEY=value file, one variable per line, sourced by bash. No export keyword needed (set -a handles it). Comments start with #. Values containing spaces or special characters should be quoted. Example:
# ~/.env โ per-machine environment, not tracked in the dotfiles repo
# Secrets and per-machine overrides. Non-secret values chezmoi already knows
# (e.g. GITHUB_USERNAME) are rendered into .bashrc directly.
EXAMPLE_API_URL=http://localhost:8080
EXAMPLE_API_TOKEN=...
# Optional โ override the default JOT_AWS_S3_BUCKET (defaults to
# ${GITHUB_USERNAME}-notes if GITHUB_USERNAME is set)
# JOT_AWS_S3_BUCKET=my-custom-bucketWhy ~/.env and not a template. Secrets and per-machine values stay outside the chezmoi-managed tree. This keeps credentials out of git and makes a machine's setup easy to diverge from the default.
What's not needed in ~/.env. Non-secret values chezmoi already collected at chezmoi init โ currently GITHUB_USERNAME โ are rendered into .bashrc by chezmoi. You can still put GITHUB_USERNAME=... in ~/.env as an override, but nothing breaks if you don't.
Tools like jot, vault, and other skills document their own required variables; add them to ~/.env as you adopt each tool.
Propagating ~/.env to other machines. It's user-managed, so copy it deliberately โ playbash put ~/.env host: (or the planned jot bundle flow) rather than checking it into the repo.
GITHUB_USERNAME is collected once by chezmoi: during chezmoi init it prompts for the githubUsername data value (default uhop, overridable from a GITHUB_USERNAME env var set when running chezmoi init). From there it flows in two places:
-
.bashrc(viadot_bashrc.tmpl): chezmoi rendersexport GITHUB_USERNAME=<value>near the top of the exports block, before~/.envis sourced โ so~/.envcan override if a machine needs a different identity. -
Chezmoi run-scripts and templates (via
[scriptEnv]in.chezmoi.toml.tmpl): used byprivate_dot_ssh/create_private_authorized_keys.tmplto pull GitHub-registered SSH keys, and by any other chezmoi-managed scripts that need it.
JOT_AWS_S3_BUCKET is derived as ${GITHUB_USERNAME}-notes only when (a) it's not already set and (b) GITHUB_USERNAME is non-empty. That means users without a GitHub identity get no bogus -notes bucket, and anyone wanting a custom bucket can just set JOT_AWS_S3_BUCKET=my-bucket in ~/.env. If jot runs with neither set, it prints a warning explaining the relationship and exits.
Sourced by .bashrc unconditionally (available to both interactive and non-interactive shells). Contains aliases, functions, and safety nets grouped by category.
Git alias completions are declared in .bash_aliases via the __git_alias_completions associative array (maps alias name to git subcommand), then wired up by .bashrc in the interactive block after bash-completion is loaded.
If doas is installed and configured (/etc/doas.conf exists), sudo is aliased to doas.
| Alias | Expansion | Description |
|---|---|---|
l |
eza |
Plain listing |
ls |
eza --grid --color auto --icons --sort=type |
Grid with icons |
ll |
eza --long --color always --icons --sort=type |
Long format |
la |
eza --grid --all ... |
All files, grid |
lla |
eza --long --all ... |
All files, long |
ltr |
eza -l -t modified -r |
Long, sorted by modification time (newest last) |
l. โ function that lists dotfiles only: l. [dir]
| Alias | Expansion |
|---|---|
.. |
z .. |
... |
z ../../ |
.... |
z ../../../ |
.4 |
z ../../../../ |
.5 |
z ../../../../../ |
~ |
z ~ |
- |
z - |
up N โ go up N directories: up 3 = cd ../../../
zl [dir] โ z into a directory and list it.
mkz / mkcd โ create directory and z into it.
zpush / zp โ pushd that matches a keyword the way z does: zp proj pushes the directory z proj would go to. pushd's own arguments (-n, +N, -N, -) and an existing directory go to pushd as they are, so zp +2 rotates the stack and zp -n proj adds proj without going there. A directory is in the stack once: when it is already there, zp moves that entry to the top, so zpop still takes you back to where you were.
zpop โ popd.
zdirs โ the directory stack in fzf, numbered as dirs -v numbers it, with the keys in a one-line legend. Typing filters the list. Enter moves the picked directory to the top and goes there, as zp does. Ctrl-R rotates the stack to it instead (pushd +N). Ctrl-D drops it from the stack (the current directory stays) and reopens the list. Del drops it too, and so does Backspace when the filter is empty. On a Mac keyboard, the key labeled Delete is Backspace, and Fn+Delete is Del. Esc closes it. With any option or argument it is plain dirs.
| Alias | What it does |
|---|---|
rm |
rm -I --preserve-root (Linux) or rm -I (macOS) |
mv |
mv -iv (interactive, verbose) |
cp |
cp -iv |
ln |
ln -i |
chown, chmod, chgrp
|
--preserve-root (Linux only) |
mkdir |
mkdir -pv (create parents, verbose) |
wget |
wget -c (resume downloads) |
Available when rsync is installed:
| Alias | Description |
|---|---|
rcp |
rsync copy with aggregate progress (--info=progress2 --no-inc-recursive --stats) |
rmv |
rsync move with aggregate progress (same flags + --remove-source-files) |
rup |
rsync update โ skip newer files on receiver (-u) |
rsy |
rsync sync โ update + delete extraneous files on receiver (-u --delete) |
Shortcuts that wrap git commands with colored output via echoRun:
| Alias | Git command | Completion |
|---|---|---|
gst |
git status |
โ |
gco |
git checkout |
โ |
gcob |
git checkout -b |
โ |
gcm |
git commit |
โ |
gbr |
git branch |
โ |
gpull |
git pull |
โ |
gpush |
git push |
โ |
gsw |
git switch (defaults to main branch) |
โ |
gme |
git merge (defaults to main branch) |
โ |
gg |
git gui |
|
gk |
gitk --all |
| Alias | What it does |
|---|---|
grep |
grep --color=auto |
egrep |
egrep --color=auto |
fgrep |
fgrep --color=auto |
diff |
diff --color=auto |
| Alias | Replacement | Notes |
|---|---|---|
cat |
bat or batcat
|
Syntax highlighting |
ping |
sudo prettyping --nolegend |
Visual ping (only if prettyping installed) |
top |
htop |
Interactive process viewer |
du |
ncdu (no args) / real du (with args) |
Interactive disk usage when browsing, standard du otherwise |
help |
tldr |
Short command examples |
duf |
duf -only local,fuse |
Disk usage overview |
bc |
bc -l |
Calculator with math library |
mount |
Pretty-print (no args) / real mount (with args) |
Shows column-formatted table when listing, passes through for actual mounts |
| Alias | Description |
|---|---|
h |
pick โ interactive command reference (see Utilities ยง pick) |
j |
jobs -l |
df |
df -kh |
dd |
dd status=progress |
free |
free -m |
env |
env | sort (no args only; with args falls through to standard env) |
path |
Print $PATH entries one per line |
mic |
micro |
lzd |
lazydocker |
lzg |
lazygit |
| Alias | Description |
|---|---|
poweroff / shutdown / poff
|
Power off (systemctl poweroff or shutdown -h now) |
reboot |
Reboot (systemctl reboot or shutdown -r now) |
| Alias | Description |
|---|---|
psmem |
Processes sorted by memory (Linux) |
psm10 |
Top 10 memory consumers (Linux) |
psr |
Top CPU-intensive processes |
psm |
Top memory-intensive processes |
pse |
Process tree (Linux) |
psg PATTERN |
Search running processes |
pst |
Processes with start time and elapsed |
oports |
List open listening ports |
oports2 |
Open ports via ss (Linux) |
| Alias/Function | Description |
|---|---|
gimme |
chown $USER:$USER โ take ownership (Linux) |
dirty |
Watch dirty memory pages (Linux) |
where "pattern" [path/wildcard] โ recursive grep with glob support, e.g., where "TODO" "src/*.js". Uses noGlob to prevent shell expansion.
upfind [args] โ run find in current directory and each parent up to /. Use -h flag to stop at $HOME.
upfd [args] โ same as upfind but uses fd instead of find.
upsearch pattern โ shortcut for upfd -gH pattern (glob, hidden files).
| Alias | Description |
|---|---|
tre |
tree excluding common junk dirs (node_modules, .git, venv, etc.) |
gre |
grep -r excluding common dirs |
nowrap [start] [-end] โ pipe filter to truncate output to terminal width. Positive arg sets start column, negative sets offset from right edge.
| Function | Description |
|---|---|
ssht host [session] |
SSH into host and attach/create tmux session |
mosht host [session] |
Same via mosh
|
ett host [session] |
Same via et (Eternal Terminal) |
kssh |
kitten ssh (Kitty) |
kssht host [session] |
Kitty SSH + tmux |
Default session name is ssh_tmux.
Both files are identical: they conditionally source .bashrc only when $BASH_VERSION is set.
-
.bash_profileโ sourced by bash login shells. Sources.bashrcto get the full interactive environment. -
.profileโ sourced by non-bash login shells (bash prefers.bash_profile). Also sources.bashrcwhen running under bash.