envocabulary v1.0.5
For every variable in your current shell, find the file and line that set it, or which subsystem 2026-9-16 04:17:34 Author: kitploit.com(查看原文) 阅读量:7 收藏

CI codecov Release

For every variable in your current shell, find the file and line that set it, or which subsystem (direnv, launchd, terminal, SSH, system) injected it. Plus a few static-file commands for the moment your shell config has sprawled across N files and backups and you've lost the plot.

I built this because I kept losing the same hour every few months tracing why some JAVA_HOME or PATH was pointing somewhere I didn't expect. which knows commands, direnv status knows direnv, launchctl getenv knows launchd. None of them tell you that ~/.zshrc:42 is the actual writer.

Works with zsh and bash on macOS, Linux, and FreeBSD. Sample output below uses ~/ for brevity; the tool prints absolute paths everywhere except report.

The "ah" moment

grep -r JAVA_HOME ~ shows every file that mentions the variable. It doesn't tell you which assignment won in the shell you're sitting in:

$ envocabulary explain --chain JAVA_HOME
JAVA_HOME
  origin   shell-file
  primary  ~/helpers.sh:3
  chain    ~/.zshrc → ~/helpers.sh
  writers
    ~/.zshenv:8
    ~/helpers.sh:3  (winner)
  value    [hidden, use --values]

Variables set through eval "$(brew shellenv)", eval "$(pyenv init -)" and friends point at the eval line. The generated code has no line of its own; the eval is what you can open in an editor.

Install

curl -fsSL https://raw.githubusercontent.com/sreckoskocilic/envocabulary/main/install.sh | sh

The script detects OS and arch, downloads the release archive, verifies its sha256 checksum, and verifies the cosign signature when cosign is installed. It installs into /usr/local/bin if that is writable, otherwise ~/.local/bin, and warns if that directory is not on your $PATH. On macOS it also clears the Gatekeeper quarantine flag.

Options: sh -s -- --version v1.0.4 to pin a version, --bin-dir DIR to choose the destination.

Or go install github.com/sreckoskocilic/envocabulary/cmd/envocabulary@latest. Note that a go install build reports dev for --version; only release binaries carry the version, commit, and build date.

Pre-built binaries and Linux packages (.deb / .rpm / .apk / .pkg.tar.zst) are on the releases page.

Commands

Live env (reads your running shell):

  • scan (default) — prints all variables in the current env grouped by origin
  • explain NAME — full attribution for one variable
  • path [VARNAME...] — per-entry attribution for colon-separated path variables; --check finds dead entries

Static files:

  • inventory — lists the shell config files in $HOME (canonical ones plus backup variants like .zshrc.bak) and counts definitions by type
  • catalog — prints those files concatenated in the order the shell reads them
  • dedup — duplicate report for exports, assigns, aliases, functions, within and across files
  • dangling — lists config entries whose target no longer exists; exits 1 when it finds any
  • lost — lists definitions that exist only in orphan/backup files
  • report — combined audit: safe-to-delete, review (dups whose value differs, plus all duplicate functions), dangling, orphaned files; --html writes a timestamped .html file into the current directory
  • clean [--full] FILE — lists comment lines that would be stripped; with --full, prints the cleaned content

Other:

  • -V, --version — print version, commit, and build date

envocabulary <cmd> -h for flags.

Exit codes: 0 on success, 1 on a runtime error or when dangling / path --check find something, 2 on a usage error.

Finding broken references

dangling lists config entries that no longer point at anything, the JAVA_HOME=/opt/jdk-i-uninstalled and source ~/dotfiles/work-old.zsh kind of leftovers:

$ envocabulary dangling
## ~/.zshrc
  ~/.zshrc:14  source   → ~/dotfiles/work-old.zsh  (source target missing)
  ~/.zshrc:42  export JAVA_HOME  → /opt/jdk-11  (path does not exist)

Tracing PATH entries

path shows where each entry in PATH (or MANPATH, FPATH, etc.) was introduced:

$ envocabulary path PATH
## PATH
  /opt/homebrew/bin       ~/.zprofile:6
  /opt/homebrew/sbin      ~/.zprofile:6
  /usr/local/bin          /etc/zprofile:11
  /usr/bin                inherited
  /bin                    inherited
  ~/.cargo/bin            ~/.zshrc:22

Entries not matched to any shell startup assignment show as inherited. That always includes /usr/bin, /bin, /usr/sbin and /sbin: they are the seed the tracer starts from, not something a file added.

--check filters to entries whose directory no longer exists and re-resolves the source against your dotfiles and /etc/paths.d, so it points at the line to edit:

$ envocabulary path --check PATH
## PATH
  /opt/homebrew/Cellar/go/1.25.1/libexec/bin  ~/.zshrc:17  (does not exist)
  /opt/pkg/env/active/bin                      /etc/paths.d/10-pmk-global:1  (does not exist)
  /Applications/VMware                         /etc/paths.d/com.vmware.fusion.public:1  (does not exist)

Exits 1 when dead entries are found, which makes it usable in scripts.

Limits

  • One assignment per line: export EDITOR=vim VISUAL=vim records EDITOR only.
  • The static commands only look at the canonical dotfiles in $HOME (.zshenv, .zprofile, .zshrc, .zlogin, .zlogout, .bashrc, .bash_profile, .profile) and their .bak/.old-style variants. They do not follow $ZDOTDIR, ~/.config/zsh, /etc, or files you source.
  • dangling skips PATH-like values and anything with an expansion in it (export GOPATH=$HOME/go); it can't resolve those statically.
  • path attribution is based on xtrace diffs. The first assignment that includes an entry claims it, even if it was carried forward via $PATH expansion rather than explicitly added.
  • Under bash, code run through eval reports the eval line plus an offset; bash has no marker for eval bodies the way zsh does.
  • Unsupported shells: fish, nu, csh/tcsh, PowerShell.

Read-only by design

envocabulary will never unset, rm, or edit your shell config. An emergency tool shouldn't be the thing that makes the emergency worse. If you want to clean things up, copy the file:line pointers and do the edits yourself. clean outputs to stdout; you do the redirect.


文章来源: https://kitploit.com/en/posts/github-sreckoskocilic-envocabulary-v105
如有侵权请联系:admin#unsafe.sh