zsh가 나를 골탕먹이고 있다
무슨 일이 일어나고 있나요?
당신이 운이 나쁘면 zsh의 tab 자동완성 기능을 실행하다가 쉘이 먹통이 되는 상황을 겪을 수 있다. 아직 이 버그에 대해 교차검증이 된 것은 아니지만, 적어도 내가 사용가능한 환경에서는 안정적으로 버그를 발생시키는 데 성공했기 때문에 여기에 한번 정리해 본다.
무슨 짓을 하면 그렇게 되는거죠?
크고 빨간 경고문: 아래 버그는 당신의 locale이 en_US.UTF-8인 경우에만
안정적으로 재현됩니다. 참고로 C, ko_KR.UTF-8, C.UTF-8인 경우엔 재현되지
않으니 주의하세요.
#!/usr/bin/env bash
# Start tmux session in detached mode with zsh
tmux new-session -d zsh -f
# Wait a moment for the session to initialize
sleep 1
tmux send-keys 'touch "A-개미허리 간다.E01.251009.108-F.mp4" "상어개인4.첫 방송.E01.251014.1080p.H264-ASDFNEWS.mp4"' Enter
tmux send-keys "autoload -U compinit; compinit; zstyle ':completion:*' matcher-list 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' 'r:|=*' 'l:|=* r:|=*'; zstyle ':completion:*' special-dirs true" Enter
tmux send-keys "rm E01" Tab Tab Tab
tmux attach
여기까지 해 보면 먹통이 된 zsh를 확인할 수 있다. 경우에 따라 Tab을 몇번 더 눌러줘야 하는 경우도 있는 것 같긴 한데…
무슨 짓을 저지른 거죠?
gdb call stack은 다음과 같다:
#0 sub_match (md=0x7fff5a3c4930, str=0x14f1b8dcede6 "\201.E01.251014.1080p.H264-F1RST.mp4", len=30, sfx=4)
at compmatch.c:2374
#1 0x000014f1b8d8be3c in join_psfx (ot=0x14f1b8dce870, nt=0x14f1b8dcf0a0, orest=0x0, nrest=0x0, sfx=4) at compmatch.c:2493
#2 0x000014f1b8d8d427 in join_clines (o=0x14f1b8dce870, n=0x14f1b8dcf0a0) at compmatch.c:2952
#3 0x000014f1b8d8460a in add_match_data (alt=0,
str=0x14f1b8dcf200 "글냁\203\274벃\254\\ 탵\203\274샽\270기탶\203\251-김샸\201첃\200샽\264\\ 갃\244냫\244.E01.251009.1080p.H264-F1RST.mp4",
orig=0x55d6d12bcbd0 "글냁\203\274벃\254\\ 탵\203\274샽\270기탶\203\251-김샸\201첃\200샽\264\\ 갃\244냫\244.E01.251009.1080p.H264-F1RST.mp4", line=0x14f1b8dcf260, ipre=0x14f1b8dced70 "", ripre=0x0, isuf=0x14f1b8dced78 "", pre=0x0,
prpre=0x14f1b8dceda8 "", ppre=0x14f1b8dced98 "", pline=0x14f1b8dce818, psuf=0x14f1b8dceda0 "", sline=0x0, suf=0x0,
flags=1, exact=0) at compcore.c:2994
#4 0x000014f1b8d82a2a in addmatches (dat=0x7fff5a3c50d0, argv=0x55d6d12b0808) at compcore.c:2547
#5 0x000014f1b8d78a30 in bin_compadd (name=0x14f1b8d51ff8 "compadd", argv=0x14f1b8d4ad10, ops=0x7fff5a3c51f0, func=0)
at complete.c:848
#6 0x000055d6a26b3b81 in execbuiltin (args=0x14f1b8d4ab18, assigns=0x0, bn=0x14f1b8d99720 <bintab>) at builtin.c:506
#7 0x000055d6a26e1efd in execcmd_exec (state=0x7fff5a3c9300, eparams=0x7fff5a3c59a0, input=0, output=0, how=18, last1=2,
close_if_forked=-1) at exec.c:4260
#8 0x000055d6a26db22a in execpline2 (state=0x7fff5a3c9300, pcode=45955, how=18, input=0, output=0, last1=0) at exec.c:2020
#9 0x000055d6a26d99f9 in execpline (state=0x7fff5a3c9300, slcode=17410, how=18, last1=0) at exec.c:1745
#10 0x000055d6a26d8afe in execlist (state=0x7fff5a3c9300, dont_change_job=1, exiting=0) at exec.c:1498
#11 0x000055d6a27172fb in execif (state=0x7fff5a3c9300, do_exec=0) at loop.c:588
#12 0x000055d6a26e14ff in execcmd_exec (state=0x7fff5a3c9300, eparams=0x7fff5a3c6430, input=0, output=0, how=18, last1=2,
close_if_forked=-1) at exec.c:4080
내가 zsh의 내부 동작에 대해 잘 아는 것은 아니라서 대충 분석하자면,
cline: 이 구조체는 현재 completion에 맞는 문자열을 기록하고 있다.join_clines: completion에 대응하는 문자열들을 모으는 역할을 하는 듯 보인다.join_psfx: 유저가 입력중인 자동완성 문자열 앞뒤로 확장 가능한지 여부를 확인한다. 이를테면 ASDF를 입력하고 Tab Tab 하는 상황에서KKKKKASDFNEWS,PPPKKASDFBAD라는 두 개의 후보가 존재한다면 ASDF라는 keyword는 KKASDF로 확장하는 것이 타당하다.sub_match: 주어진 문자열보다 더 긴 매치가 존재하는지 확인하고 확장가능한 길이를 반환한다.
여기서 sub_match의 반환값이 항상 >=0이어야 하는데, 하필 위에 주어진
상황에서는 -1을 반환할 수도 있는 경우가 존재한다.
어떤 경우에 -1이 반환되나요?
sub_match가 suffix 모드로 실행되는데 후보 문자열에 유니코드 문자의 일부 - 즉,
한글 한글자는 3바이트인데 뒤 1~2바이트 - 만 후보 문자열에 포함되는 경우 해당
문자열을 빼라는 의미로 -1이나 -2가 반환될 수 있다. (위 예에선 -1이 반환되는데,
이론적으론 -2가 반환될 수도 있겠지만 확인해보진 않음)
그럼 이제 어쩌죠?
대충 땜방치는 fix를 제출했는데 관리자가 버그 재현이 안된다고 해서… 어차피 거의 발생하지 않는 버그이긴 하니 대충 짬처리되거나 어찌어찌 고쳐지거나 둘 중 하나일 것이다.