쉘에서 명령어 자동 완성 제대로 활용하기

쉘에서 명령어 자동 완성 제대로 활용하기

터미널에서 작업하다 보면 길고 복잡한 명령어를 반복해서 입력하게 됩니다. docker compose --file docker-compose.production.yml up --detach 같은 명령어를 한 글자씩 정확히 치는 건 너무 비효율적이죠. 그래서 우리에게는 Tab 키가 있습니다.

Tab 키로 명령어 이름과 파일 경로를 완성하는 건 누구나 알고 있죠. 그런데 쉘의 자동 완성은 그게 끝이 아닙니다. 서브 명령어, 옵션 플래그, 환경 변수, 심지어 Git 브랜치 이름까지 Tab 한 번으로 완성할 수 있는데요. 이 글에서는 Bash와 Zsh에서 자동 완성을 켜는 방법, 자주 쓰는 도구의 자동 완성을 추가하는 방법, 그리고 직접 자동 완성 스크립트를 작성하는 방법까지 차례대로 살펴보겠습니다.

Tab 키가 기본으로 해주는 일

쉘이 처음 설치된 상태에서도 Tab 키는 어느 정도 동작합니다. 어떤 부분이 자동으로 되는지 먼저 짚어 보겠습니다.

명령어 이름을 부분적으로 입력하고 Tab을 누르면 PATH에 있는 실행 파일 중에서 일치하는 것을 찾아 완성해 줍니다.

ec  # Tab → echo

파일이나 디렉터리 경로도 자동으로 채워 줍니다.

cd Doc  # Tab → cd Documents/

후보가 여러 개일 때는 Tab을 한 번 더 누르면 가능한 후보 목록을 보여줍니다.

$ git che  # Tab Tab
checkout   cherry     cherry-pick

환경 변수도 $ 뒤에서 Tab을 누르면 자동 완성됩니다.

echo $HO  # Tab → echo $HOME

여기까지는 별다른 설정 없이도 작동하는 기본 동작입니다. 하지만 git checkout 다음에 브랜치 이름을 자동 완성하거나, npm run 다음에 스크립트 이름을 완성하려면 추가 설정이 필요합니다.

Bash에서 자동 완성 활성화하기

Bash는 기본 상태에서 명령어와 파일만 완성합니다. 서브 명령어나 옵션까지 완성하려면 bash-completion 패키지를 설치해야 합니다.

macOS에서는 Homebrew로 설치합니다.

brew install bash-completion@2

설치 후에는 ~/.bashrc~/.bash_profile에 다음을 추가합니다. 설정 파일이 어떻게 다른지 헷갈린다면 쉘 설정 파일 가이드를 참고하세요.

[[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh"

Ubuntu 같은 Linux 배포판에서는 보통 기본으로 설치되어 있습니다. 없다면 패키지 매니저로 설치하면 됩니다.

sudo apt install bash-completion

설치 후 새 터미널을 열거나 source ~/.bashrc를 실행하면 적용됩니다. 이제 git 뒤에 공백을 치고 Tab을 누르면 add, commit, push 같은 서브 명령어 목록이 나옵니다.

bash-completion이 제공하는 자동 완성 스크립트는 /usr/share/bash-completion/completions/(Linux)나 /opt/homebrew/etc/bash_completion.d/(macOS) 디렉터리에 들어 있습니다. 도구별로 파일이 하나씩 있고, 쉘이 시작될 때 필요한 것을 로드합니다.

Zsh에서 자동 완성 활성화하기

Zsh의 자동 완성 시스템은 Bash보다 훨씬 정교하지만, 처음에는 기본 상태로 켜져 있지 않을 수 있습니다. ~/.zshrc에 다음 두 줄을 추가해 활성화합니다.

autoload -Uz compinit
compinit

autoload는 Zsh의 함수 로딩 메커니즘이고, compinit은 자동 완성 시스템을 초기화하는 함수입니다. 한 번만 호출하면 그 뒤로는 fpath에 등록된 모든 자동 완성 스크립트가 활성화됩니다.

Oh My Zsh를 쓰고 있다면 이미 compinit이 호출되어 있으므로 별도 설정이 필요 없습니다. 대신 Oh My Zsh의 plugins 배열에 원하는 도구를 추가하기만 하면 됩니다.

plugins=(git docker kubectl npm)

Zsh 자동 완성에는 Bash에는 없는 편리한 기능이 몇 가지 있습니다. 대표적으로 대소문자 구분 없는 완성과 부분 문자열 매칭이 그렇죠. 다음 설정을 ~/.zshrccompinit 호출 위에 추가해 보세요.

zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|=*' 'l:|=* r:|=*'

이러면 cd doc을 입력하고 Tab을 눌렀을 때 Documents로 완성되고, kbectl처럼 글자가 빠져도 kubectl을 찾아 줍니다.

도구별 자동 완성 추가하기

쉘 자동 완성이 켜졌어도 모든 CLI 도구가 자동으로 지원되는 건 아닙니다. 각 도구가 제공하는 자동 완성 스크립트를 별도로 등록해야 합니다. 다행히 요즘 나오는 도구는 대부분 completion 서브 명령어로 자동 완성 스크립트를 직접 만들어 줍니다.

GitHub CLI의 경우 다음과 같이 등록합니다.

# Bash
echo 'eval "$(gh completion -s bash)"' >> ~/.bashrc

# Zsh
echo 'eval "$(gh completion -s zsh)"' >> ~/.zshrc

kubectl도 같은 패턴을 따릅니다.

# Zsh
echo 'source <(kubectl completion zsh)' >> ~/.zshrc

Bun이나 Deno 같은 런타임도 비슷합니다.

bun completions  # 자동으로 쉘을 감지해 설치

자동 완성을 직접 파일로 저장해 두고 싶다면 fpath에 들어가는 디렉터리에 _도구이름 형식으로 저장합니다.

mkdir -p ~/.zsh/completions
kubectl completion zsh > ~/.zsh/completions/_kubectl

그리고 ~/.zshrc에서 fpath에 그 디렉터리를 추가합니다.

fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit
compinit

이 방법이 매번 eval로 실행하는 것보다 쉘 시작 속도가 빠릅니다. eval 방식은 쉘이 켜질 때마다 명령어를 실행해서 스크립트를 만들어 내는 반면, 파일로 저장해 두면 그 비용이 없으니까요.

자동 완성 직접 작성하기

내가 만든 스크립트나 별칭에 자동 완성을 붙이고 싶을 때도 있습니다. 간단한 예로, 환경 이름을 받는 배포 스크립트를 생각해 봅시다.

deploy production
deploy staging
deploy development

Bash에서는 complete 명령어를 사용해 후보 목록을 직접 지정할 수 있습니다.

_deploy_completions() {
  local cur=${COMP_WORDS[COMP_CWORD]}
  COMPREPLY=($(compgen -W "production staging development" -- "$cur"))
}
complete -F _deploy_completions deploy

COMP_WORDS는 현재 입력된 단어들의 배열이고, COMP_CWORD는 커서가 있는 단어의 인덱스입니다. compgen -W는 주어진 단어 목록 중에서 현재 입력에 매칭되는 것만 골라 줍니다.

Zsh에서는 더 간결하게 쓸 수 있습니다.

_deploy() {
  _arguments '1: :(production staging development)'
}
compdef _deploy deploy

_arguments는 Zsh의 자동 완성 헬퍼 함수로, 위치:설명:후보 형식으로 인자를 정의합니다. compdef는 만든 함수를 특정 명령어에 연결합니다.

좀 더 복잡한 경우, 가령 옵션에 따라 후보가 달라져야 한다면 함수 안에서 분기 처리를 하면 됩니다.

_deploy() {
  local -a envs
  envs=(production staging development)

  case $CURRENT in
    2) _arguments "1: :($envs)" ;;
    3) _arguments "2: :(--dry-run --force --verbose)" ;;
  esac
}
compdef _deploy deploy

자동 완성 함수는 보통 ~/.zsh/completions/_명령어이름 파일로 저장하고, 위에서 본 것처럼 fpath에 디렉터리를 추가해서 로드합니다.

한 단계 더 편리한 자동 완성

기본 자동 완성으로 만족하지 못한다면 보조 도구를 붙여 볼 수 있습니다.

zsh-autosuggestions는 명령어를 입력하면 히스토리에 있던 비슷한 명령어를 회색으로 미리 보여 줍니다. 오른쪽 화살표 키를 누르면 그대로 완성되죠.

brew install zsh-autosuggestions
echo 'source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh' >> ~/.zshrc

fzf-tab은 Tab 키를 누를 때 나오는 후보 목록을 fzf의 인터랙티브 인터페이스로 바꿔 줍니다. 방향 키로 후보를 탐색하고, 파일이라면 미리 보기까지 띄울 수 있습니다.

git clone https://github.com/Aloxaf/fzf-tab ~/.zsh/fzf-tab
echo 'source ~/.zsh/fzf-tab/fzf-tab.plugin.zsh' >> ~/.zshrc

zsh-syntax-highlighting은 자동 완성은 아니지만 같이 쓰면 좋습니다. 명령어가 유효한지 색상으로 알려 주기 때문에 오타를 입력 단계에서 잡을 수 있습니다.

셋 다 켜 두면 터미널에서 명령어를 입력하는 느낌이 IDE에서 코드를 쓰는 것과 꽤 비슷해집니다.

자동 완성이 안 될 때

자동 완성이 갑자기 동작하지 않을 때 가장 흔한 원인은 compinit 캐시 문제입니다. ~/.zcompdump 파일을 지우고 새 터미널을 열어 보세요.

rm ~/.zcompdump*

권한 경고가 나오는 경우도 있습니다. compinitfpath의 디렉터리 권한을 검사하기 때문인데요, 다음 명령어로 권한을 정리합니다.

compaudit | xargs chmod g-w

자동 완성 스크립트가 등록됐는지 확인하려면 whichwhence를 써 봅니다.

which _git  # /usr/share/zsh/functions/Completion/Unix/_git

빈 결과가 나오면 해당 스크립트가 fpath에 없거나 compinit이 인식하지 못한 상태입니다.

마치며

쉘 자동 완성은 작은 기능처럼 보여도 하루 동안 누르는 Tab 키가 쌓이면 절약되는 시간이 만만치 않습니다. 처음에는 compinit 한 줄만 추가해도 충분합니다. 익숙해지면 도구별 자동 완성 스크립트를 등록해 보고, 더 나아가서 직접 만든 스크립트에도 자동 완성을 붙여 보세요.

자동 완성이 잘 동작하는 터미널 환경은 한번 만들어 두면 두고두고 생산성을 올려 줍니다. Oh My Zsh로 출발해서 Bash 파라미터 확장 같은 쉘 자체의 기능까지 익혀 가면 터미널 작업이 점점 즐거워질 거예요.

더 자세한 내용은 Zsh Completion System 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord