
envocabulary v1.0.5
모든 셸 환경 변수에 대해 정확한 파일 및 줄 출처를 추적합니다. macOS, Linux, FreeBSD의 zsh 및 bash에서 셸 설정 파일을 감사하여 죽은 항목, 중복, 고아 파일을 찾아냅니다.
envocabulary
현재 셸에 있는 모든 변수에 대해, 그 변수를 설정한 파일과 줄 번호를 찾아주거나, 어떤 하위 시스템(direnv, launchd, 터미널, SSH, 시스템)이 주입했는지 알려줍니다. 또한 셸 설정이 N개의 파일과 백업으로 흩어져 더 이상 파악이 안 될 때를 위한 몇 가지 정적 파일 명령도 제공합니다.
이 도구를 만든 이유는 몇 달에 한 번씩 JAVA_HOME이나 PATH가 예상치 못한 곳을 가리키는 이유를 추적하다가 매번 같은 시간을 낭비했기 때문입니다. which는 명령어를 알고, direnv status는 direnv를 알고, launchctl getenv는 launchd를 압니다. 하지만 그중 어느 것도 ~/.zshrc:42가 실제 작성자라는 사실을 알려주지 않습니다.
macOS, Linux, FreeBSD에서 zsh와 bash를 지원합니다. 아래 샘플 출력은 간결함을 위해 ~/를 사용하지만, 도구는 report를 제외하고 모든 곳에서 절대 경로를 출력합니다.
"아하" 하는 순간
grep -r JAVA_HOME ~는 변수를 언급하는 모든 파일을 보여줍니다. 하지만 지금 앉아 있는 셸에서 어떤 할당이 이겼는지는 알려주지 않습니다:
$ 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]
eval "$(brew shellenv)", eval "$(pyenv init -)" 등을 통해 설정된 변수는 eval 줄을 가리킵니다. 생성된 코드에는 자체적인 줄이 없으며, 편집기에서 열 수 있는 것은 eval입니다.
설치
curl -fsSL https://raw.githubusercontent.com/sreckoskocilic/envocabulary/main/install.sh | sh
이 스크립트는 OS와 아키텍처를 감지하고, 릴리스 아카이브를 다운로드하며, sha256 체크섬을 검증하고, cosign이 설치되어 있으면 cosign 서명도 검증합니다. 쓰기 가능하면 /usr/local/bin에 설치하고, 그렇지 않으면 ~/.local/bin에 설치하며, 해당 디렉터리가 $PATH에 없으면 경고합니다. macOS에서는 Gatekeeper 격리 플래그도 해제합니다.
옵션: sh -s -- --version v1.0.4로 버전을 고정하고, --bin-dir DIR로 설치 대상을 선택할 수 있습니다.
또는 go install github.com/sreckoskocilic/envocabulary/cmd/envocabulary@latest. go install 빌드는 --version에 대해 dev를 보고한다는 점에 유의하세요. 릴리스 바이너리만 버전, 커밋, 빌드 날짜를 포함합니다.
미리 빌드된 바이너리와 Linux 패키지(.deb / .rpm / .apk / .pkg.tar.zst)는 릴리스 페이지에 있습니다.
명령어
라이브 환경(실행 중인 셸을 읽음):
scan(기본값) — 현재 환경의 모든 변수를 origin별로 그룹화하여 출력explain NAME— 하나의 변수에 대한 전체 출처 정보path [VARNAME...]— 콜론으로 구분된 경로 변수의 항목별 출처 정보;--check는 죽은 항목을 찾음
정적 파일:
inventory—$HOME에 있는 셸 설정 파일(정식 파일과.zshrc.bak같은 백업 변형)을 나열하고 유형별로 정의 개수를 집계catalog— 셸이 읽는 순서대로 해당 파일들을 이어붙여 출력dedup— 파일 내 및 파일 간의 export, assign, alias, function에 대한 중복 보고서dangling— 대상이 더 이상 존재하지 않는 설정 항목을 나열; 발견 시 종료 코드 1lost— 고아/백업 파일에만 존재하는 정의를 나열report— 통합 감사: 삭제해도 안전한 것, 검토 필요(값이 다른 중복, 모든 중복 함수), dangling, 고아 파일;--html은 현재 디렉터리에 타임스탬프가 붙은.html파일을 작성clean [--full] FILE— 제거될 주석 줄을 나열;--full을 사용하면 정리된 내용을 출력
기타:
-V,--version— 버전, 커밋, 빌드 날짜 출력
플래그는 envocabulary <cmd> -h를 참조하세요.
종료 코드: 성공 시 0, 런타임 오류 또는 dangling / path --check가 무언가를 발견했을 때 1, 사용법 오류 시 2.
깨진 참조 찾기
dangling은 더 이상 아무것도 가리키지 않는 설정 항목, 즉 JAVA_HOME=/opt/jdk-i-uninstalled나 source ~/dotfiles/work-old.zsh 같은 잔여물을 나열합니다:
$ 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)
PATH 항목 추적
path는 PATH(또는 MANPATH, FPATH 등)의 각 항목이 어디에서 도입되었는지 보여줍니다:
$ 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
어떤 셸 시작 할당과도 일치하지 않는 항목은 inherited로 표시됩니다. 여기에는 항상 /usr/bin, /bin, /usr/sbin, /sbin이 포함됩니다. 이들은 파일이 추가한 것이 아니라 추적기가 시작하는 시드이기 때문입니다.
--check는 디렉터리가 더 이상 존재하지 않는 항목만 필터링하고, dotfiles와 /etc/paths.d를 기준으로 소스를 다시 확인하여 편집해야 할 줄을 가리킵니다:
$ 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)
죽은 항목이 발견되면 종료 코드 1을 반환하므로 스크립트에서 사용할 수 있습니다.
제한 사항
- 한 줄에 하나의 할당:
export EDITOR=vim VISUAL=vim은EDITOR만 기록합니다. - 정적 명령은
$HOME의 정식 dotfile(.zshenv,.zprofile,.zshrc,.zlogin,.zlogout,.bashrc,.bash_profile,.profile)과 그.bak/.old스타일 변형만 봅니다.$ZDOTDIR,~/.config/zsh,/etc, 또는source한 파일은 따라가지 않습니다. dangling은 PATH 같은 값과 확장이 포함된 것(export GOPATH=$HOME/go)은 건너뜁니다. 이런 것들은 정적으로 확인할 수 없습니다.path출처 정보는 xtrace diff를 기반으로 합니다. 항목을 포함하는 첫 번째 할당이 그 항목을 차지하며, 명시적으로 추가된 것이 아니라$PATH확장을 통해 이어져 온 경우에도 마찬가지입니다.- bash에서는
eval을 통해 실행된 코드가eval줄과 오프셋을 보고합니다. bash에는 zsh처럼 eval 본문에 대한 마커가 없습니다. - 지원하지 않는 셸: fish, nu, csh/tcsh, PowerShell.
설계상 읽기 전용
envocabulary는 절대 unset, rm, 또는 셸 설정을 편집하지 않습니다. 비상 도구가 비상 상황을 더 악화시키는 원인이 되어서는 안 됩니다. 정리를 원한다면 file:line 포인터를 복사해서 직접 편집하세요. clean은 stdout으로 출력하며, 리다이렉션은 직접 하시면 됩니다.