콘텐츠로 건너뛰기

맥 터미널에서 대소문자 구분 없이 Tab 자동완성하기 (Zsh)

최근 Mac mini에 Neovim과 LazyVim을 설치하면서 터미널 환경을 하나씩 정리하고 있다.

그런데 작업하다 보니 은근히 거슬리는 문제가 하나 있었다.

터미널에서 파일이나 디렉터리 이름을 자동완성하려고 Tab 키를 눌렀는데, 대소문자가 다르면 원하는 대로 완성되지 않는 것이다.

예를 들어 현재 디렉터리에 README.md 파일이 있다고 가정하자.

nvim re

여기서 Tab을 누르면 당연히 README.md로 완성될 줄 알았다. 그런데 반응이 없다.

파일명을 README.md처럼 대문자로 시작해야 인식하는 상황이었던 것이다.

물론 ls로 파일명을 확인하거나 대소문자를 정확하게 입력하면 된다. 하지만 자동완성을 쓰는 이유가 이런 귀찮은 작업을 줄이기 위해서 아닌가?

다행히 Zsh에서는 설정 한 줄만으로 대소문자를 구분하지 않는 자동완성을 구현할 수 있다.

이번 글에서는 macOS 기본 셸인 Zsh에서 대소문자 구분 없는 Tab 자동완성을 설정하는 방법과, 설정했는데도 작동하지 않을 때 해결하는 방법을 정리해 보았다.


1. 왜 대소문자를 구분할까?

먼저 터미널의 자동완성 기능부터 알아보자.

macOS는 기본적으로 Zsh(Z Shell)를 사용한다. Zsh에는 사용자가 입력한 문자열을 바탕으로 명령어, 파일명, 디렉터리 등을 찾아 완성하는 기능이 있다.

예를 들어 다음과 같은 파일들이 있다고 가정하자.

project/
├── README.md
├── Application/
├── config.yaml
└── Dockerfile

기본적인 대소문자 구분 자동완성에서는 다음과 같은 차이가 발생할 수 있다.

입력자동완성
cat REREADME.md
cat re일치하는 후보 없음
cd AppApplication/
cd app일치하는 후보 없음

물론 macOS 파일 시스템 자체는 일반적으로 대소문자를 구분하지 않도록 구성되어 있다.

그렇다면 왜 이런 일이 발생할까?

파일 시스템이 파일을 찾는 방식과 Zsh가 자동완성 후보를 검색하는 방식은 서로 다르기 때문이다.

파일 시스템에서 readme.md와 README.md를 동일하게 취급하더라도, Zsh 자동완성은 별도의 매칭 규칙에 따라 후보를 검색한다.

즉, 파일 시스템을 변경할 필요 없이 Zsh의 매칭 규칙만 바꾸면 해결되는 문제다.

2. 해결 방법: ~/.zshrc 수정하기

Zsh의 사용자별 설정은 일반적으로 홈 디렉터리의 .zshrc 파일에서 관리한다.

먼저 현재 사용 중인 셸을 확인해 보자.

echo $SHELL

다음과 같이 출력된다면 Zsh를 사용하고 있는 것이다.

/bin/zsh

이제 설정 파일을 열어보자.

nvim ~/.zshrc

Neovim을 설치하지 않았다면 macOS에 기본으로 제공되는 nano를 사용해도 된다.

nano ~/.zshrc

파일 하단에 다음 내용을 추가한다.

# Zsh 자동완성 초기화
autoload -Uz compinit
compinit

# 대소문자 구분 없는 Tab 자동완성
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'

이미 compinit가 설정되어 있다면 중복해서 추가할 필요는 없다.

또한 Oh My Zsh를 사용하고 있다면 일반적으로 자동완성 시스템이 초기화되므로, 기존 설정을 유지하면서 zstyle 한 줄만 추가하면 된다.

설정을 저장한 후 터미널에서 다음 명령어를 실행한다.

source ~/.zshrc

source는 설정 파일을 현재 셸에서 다시 읽어들이는 명령어다. 터미널을 종료했다가 다시 열지 않아도 변경 사항을 즉시 적용할 수 있다.

이제 끝이다!

3. 설정한 코드의 의미

사실 필요한 코드는 몇 줄 되지 않는다. 하지만 무슨 역할을 하는지는 알고 넘어가자.

3.1 compinit: 자동완성 시스템 초기화

autoload -Uz compinit
compinit

Zsh의 자동완성 시스템을 초기화하는 코드다.

  • autoload: 필요한 함수를 사용할 때 로드하도록 등록한다.
  • -Uz: 함수 로딩 시 별칭 확장 문제를 방지하기 위한 옵션이다.
  • compinit: Zsh의 자동완성 시스템을 초기화한다.

이 과정을 통해 Zsh는 파일과 디렉터리뿐 아니라 명령어별로 정의된 자동완성 기능도 사용할 수 있게 된다.

3.2 zstyle: 자동완성 매칭 규칙 변경

핵심은 다음 한 줄이다.

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

조금 복잡해 보이지만 나누어 보면 어렵지 않다.

설정의미
zstyleZsh의 동작 스타일 설정
:completion:*자동완성 컨텍스트 전반에 적용
matcher-list자동완성 후보를 비교할 규칙
m:{a-zA-Z}={A-Za-z}알파벳의 대소문자를 구분하지 않고 매칭

여기서 중요한 부분은 matcher-list다.

Zsh가 자동완성 후보를 탐색할 때 사용자가 입력한 문자와 실제 후보의 문자를 어떤 기준으로 비교할 것인지 결정한다.

m:{a-zA-Z}={A-Za-z}는 대응하는 영문 대문자와 소문자를 서로 매칭할 수 있도록 설정한다.

따라서 아래와 같은 입력을 모두 인식할 수 있다.

read  → README.md
READ  → README.md
ReAd  → README.md

단, 이것은 어디까지나 자동완성 시 후보를 찾는 방식을 변경하는 것이다.

실제 파일명이 소문자로 변경되는 것도 아니고, 모든 프로그램의 대소문자 구분 동작 자체가 바뀌는 것도 아니다.

4. 설정했는데도 자동완성이 안 된다면?

나는 처음에 zstyle 설정만 추가했지만, 기대한 대로 작동하지 않았다.

그래서 자동완성 시스템을 직접 초기화하고, nvim 명령어에 파일 자동완성 기능을 연결해 보았다.

터미널에서 다음 명령어를 순서대로 실행한다.

autoload -Uz compinit
compinit
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
compdef _files nvim

다시 nvim re를 입력한 뒤 Tab 키를 누르니 정상적으로 README.md가 자동완성되었다.

여기서 새롭게 등장한 명령어가 있다.

compdef _files nvim

compdef는 특정 명령어에 어떤 자동완성 함수를 사용할 것인지 지정하는 명령어다.

위 설정은 nvim의 인자를 자동완성할 때 _files 함수를 사용하도록 지정한다.

_files는 파일 및 디렉터리 경로를 자동완성하는 함수다.

만약 glow라는 Markdown 뷰어에서도 동일한 기능을 사용하고 싶다면 다음과 같이 작성할 수 있다.

compdef _files nvim glow

물론 이 설정이 모든 명령어에 필요한 것은 아니다.

zstyle ':completion:*'가 전역적인 매칭 규칙을 설정한다면, compdef는 특정 명령어에 어떤 자동완성 기능을 연결할지 지정하는 것이다.

따라서 cd, cat, ls처럼 기존에 자동완성이 잘 작동하는 명령어에는 별도로 compdef를 추가할 필요가 없다.

또한 명령어에 이미 전용 자동완성이 등록되어 있다면 _files로 덮어쓰지 않는 편이 좋다. 기존의 옵션 및 하위 명령어 자동완성 기능이 사라질 수 있기 때문이다.

5. 최종 설정

내 환경에서 사용한 설정을 정리하면 다음과 같다.

# Zsh 자동완성 초기화
autoload -Uz compinit
compinit

# 모든 Zsh 자동완성에서 대소문자 구분 무시
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'

# 파일 자동완성이 필요한 명령어에만 추가
compdef _files nvim glow

위 설정은 Zsh 기본 환경을 기준으로 작성했다.

Oh My Zsh 등을 사용하고 있다면 기존 설정에 자동완성 초기화 코드가 있는지 먼저 확인하고, 필요한 부분만 추가하자.

설정 파일에 동일한 zstyle 코드를 여러 번 넣을 필요도 없다.

설정 적용은 다음 한 줄이면 된다.

source ~/.zshrc

제대로 적용되었는지 확인하기

현재 매칭 설정은 아래 명령어로 확인할 수 있다.

zstyle -L ':completion:*' matcher-list

Tab 키가 자동완성 기능에 연결되어 있는지도 확인할 수 있다.

bindkey '^I'

^I는 Tab 키에 해당한다.

일반적인 Zsh 자동완성 환경에서는 expand-or-complete 등의 자동완성 위젯이 연결되어 있다.

6. 실제 사용 결과

이제 다음과 같이 대소문자를 구분하지 않고 파일과 디렉터리를 자동완성할 수 있다.

입력 후 Tab완성 결과 예시
nvim renvim README.md
cat readcat README.md
cd appcd Application/
ls appls Application/
glow reglow README.md

실제로 파일명은 README.md이지만, 소문자인 re만 입력해도 자동완성된다.

또한 nvim에서만 작동하는 것이 아니라 Zsh의 자동완성 시스템을 사용하는 다른 명령어에도 매칭 규칙이 적용된다.

다만 일치하는 파일이 여러 개라면 바로 하나로 완성되지 않고 후보 목록이 표시될 수 있다.

그리고 모든 프로그램이 Zsh의 기본 자동완성 시스템을 사용하는 것은 아니므로, 프로그램별로 별도 설정이 필요한 경우도 있다.

마치며

터미널 환경을 꾸미다 보면 Oh My Zsh, 플러그인, 프롬프트 테마 등 손댈 것이 끝도 없다.

그런데 막상 매일 사용하는 환경에서는 이런 작은 설정 하나가 더 크게 체감되는 것 같다.

이번에도 단순히 Tab 키를 누를 때 대소문자를 구분하지 않게 하고 싶었을 뿐인데, 그 과정에서 Zsh의 compinit, zstyle, compdef가 각각 어떤 역할을 하는지도 알게 되었다.

정리하면 다음과 같다.

  • compinit은 자동완성 시스템을 초기화한다.
  • zstyle은 자동완성 후보를 매칭하는 규칙을 변경한다.
  • compdef는 특정 명령어에 자동완성 함수를 연결한다.

매번 정확한 대소문자를 입력하는 것이 별일 아닐 수도 있다. 하지만 하루에도 수십 번씩 사용하는 기능이라면 이야기가 달라진다.

이제 README.md를 열기 위해 굳이 Shift 키를 누르지 않아도 된다.

작은 설정 하나지만 터미널을 사용하는 일이 한결 편해졌다.

P.S. 개발 환경 설정은 끝이 없는 것 같다. Neovim을 설치하다가 터미널 설정까지 바꾸고 있으니 말이다.


참고자료

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다