Canvas 1 Layer 1

증분검색을 사용한 텍스트 필터링 도구 페코
셸(Bash, Zsh) 위에서 증분검색 활용하기

들어가며: 커맨드라인 인터페이스의 매력

커맨드라인 인터페이스는 강력하고 매력적이지만 원시적인 도구로 오해 받곤 합니다. 커맨드라인 인터페이스가 원시적으로 느껴지는 가장 큰 이유로는 명령어를 통해서만 사용이 가능하다는 점을 들 수 있습니다. 하지만 이러한 단점이 곧 커맨드라인 인터페이스의 장점이기도 합니다. 셸 위에서는 다수의 프로그램들이 STDINSTDOUT만으로 데이터를 주고받을 수 있습니다. 이는 셸 위에서 사용하는 애플리케이션들의 입출력에 다른 프로그램 통해 개입할 수 있다는 의미이고 모든 명령어의 사용성을 개선할 수 있다는 의미이기도 합니다. 반면에 애플리케이션의 단절을 전제로 하는 GUI에서는 OS에서 지원해주지 않는 경우 이러한 장점을 누릴 수 없습니다.

페코Peco는 커맨드라인 인터페이스를 개선해주는 도구 중 하나입니다. 페코는 개별적으로 사용가능한 인터렉티브 데이터 필터링 도구인 동시에, 셸의 사용성 개선에 활용할 수도 있습니다. 이 글에서는 페코의 기본적인 사용법과 셸의 히스토리 검색과 결합해서 사용하는 방법에 대해서 소개합니다.

페코(Peco) - 커맨드라인 인터페이스 증분검색 도구

페코Peco는 강력한 데이터 필터링 도구입니다. 좀 더 정확히는 증분검색을 사용하는 데이터 필터링 도구입니다. 페코가 나오기 전에 같은 목적으로 만들어진 파이썬 기반의 퍼콜Percol이라는 도구가 있었습니다. 퍼콜의 경우 파이썬Python으로 작성 되었기 때문에 성능이나 멀티 플랫폼 지원 면에서 불리한 면이 있습니다. 이러한 제약에서 벗어나기 위해 lestrrat 씨가 고 프로그래밍 언어(Go)로 퍼콜을 재구현한 애플리케이션이 바로 페코입니다. 페코는 바이너리 파일만 있으면 실행할 수 있기 때문에 윈도우, 리눅스, 맥OS 어디서든 바로 사용할 수 있습니다.

증분검색incremental search이란 단어가 낯설게 느껴질 수도 있습니다. 페코를 소개하기에 앞서 먼저 증분검색이 무엇인지부터 살펴보겠습니다. 증분검색은 인터렉티브한 검색 방법입니다. 이 방법을 사용하면 완전한 키워드를 통해서 검색 결과를 제공받는 대신 키워드를 입력하는 과정에서 검색결과를 실시간으로 제공받습니다. 예를 들어 apple 단어를 검색하기 위해서 a 입력합니다. 그러면 a에 매칭이 되는 모든 단어가 검색 결과로 나옵니다. 다음으로 ap를 입력하면 ap에 매칭이 되는 모든 단어가 검색이 되는 식입니다.

증분검색은 이미 널리 사용되고 있으며, 유용한 인터페이스 패턴 중 하나이기도 합니다. 검색어 자동완성과 애플리케이션 런처는 물론 대부분의 웹브라우저와 텍스트 에디터들이 이 기능을 지원하고 있습니다. 웹브라우저 중에는 파이어폭스Firefox가 본문 검색에서 이 기능을 선구적으로 지원했습니다. 웹페이지 상에서 찾기 도구를 사용하면 사용자의 입력을 진행하는 과정에서 웹 페이지에서 매치가 되는 모든 부분이 하이라이트 됩니다. 과거에는 단지 이 기능을 사용하기 위해서 파이어폭스를 사용했다고 말했을 정도로 강력한 기능입니다. 이해를 돕기 위해 간단한 예제를 살펴보겠습니다.

파이어폭스의 증분검색 예제
파이어폭스의 증분검색 예제

페코는 증분검색을 통한 텍스트 검색과 필터링을 셸에서 사용 가능하도록 지원해줍니다. 그럼 이제 본격적으로 페코 이야기로 넘어가 보겠습니다.

페코(Peco) 설치

페코 릴리즈 페이지에서는 플랫폼 별로 실행가능한 바이너리 파일을 압축 파일 형태로 제공합니다. 이를 다운받아 적절한 위치에 압축을 풀면 설치는 완료됩니다. 예를 들어 맥OS(macOS)를 사용하고 있다면 다음과 같이 설치할 수 있습니다.*

* 어느 위치에서든지 경로 지정없이 실행하고 싶다면 $PATH 환경변수 상의 디렉터리로 복사하거나 링크를 걸어야합니다.

$ cd /tmp/
$ wget https://github.com/peco/peco/releases/download/v0.3.5/peco_darwin_amd64.zip
$ unzip peco_darwin_amd64.zip

이외에 맥OS의 홈브류Homebrew나 윈도우즈Windows초코레이티Chocolatey와 같은 패키지 관리자로 설치를 할 수도 있습니다.

# macOS
$ brew tap peco/peco
$ brew install peco

# Windows
c:\> choco install peco

설치가 정상적으로 끝났다면, 버전을 확인해봅니다.

$ peco --version
peco: v0.3.5

기본적인 사용법

페코는 표준 출력을 입력으로 받아서 증분검색을 수행합니다. 다음 예제는 맥OS의 시스템 로그를 출력해서 페코에 넘겨주는 명령어입니다.

$ cat /var/log/systom.log | peco

cat을 사용해 /var/log/system.log 파일의 내용을 출력하고, 이 출력 내용을 peco|(파이프)를 사용해 입력으로 넘겨주고 있습니다. 이 명령어를 실행하면 다음과 같은 화면을 볼 수 있습니다.

로그 파일을 입력으로 받아 페코를 실행한 화면
로그 파일을 입력으로 받아 페코를 실행한 화면

이 상태에서 글자를 입력하면 증분검색을 해나갑니다. 다음과 같이 글자를 입력해나감에 따라 조금씩 검색이 진행되는 것을 볼 수 있습니다.

페코에서 firefox를 검색한 결과. 로그 파일에서 필터링 결과만 출력된다
페코에서 firefox를 검색한 결과. 로그 파일에서 필터링 결과만 출력된다

여러 키워드를 동시에 검색하는 것도 가능합니다.

페코의 다중 키워드 검색 결과
페코의 다중 키워드 검색 결과

Ctrl + R 키로 검색 모드를 변경할 수 있습니다. 검색 모드로는 IgnoreCase, CaseSensitive, SmartCase 등을 지원하며, Regexp 모드에서는 정규표현식을 사용할 수 있다.

페코의 정규표현식 검색 결과
페코의 정규표현식 검색 결과

이런 식으로 로그를 빠르고 쉽게 필터링할 수 있으며, 최종적으로 원하는 결과를 선택하면 표준출력으로 전달해줍니다.

ps의 출력 결과에서 프로세스를 찾아 종료하기

앞의 예에서 살펴보았듯이 페코로 필터링한 결과를 선택하면 이 내용을 표준 출력으로 내보냅니다. 따라서 페코로 필터링한 결과를 쉽게 다른 프로세스의 표준 입력으로 넘겨주는 것이 가능합니다. 이를 통해서 다양한 작업이 가능하다. 다음 예제는 peco로 원하는 프로세스를 찾아서 프로세스 ID를 출력하는 명령어입니다.

$ ps -ef | peco | awk '{ print $2 }'

이 명령어를 실행하면 ps -ef의 출력 결과를 바탕으로 페코가 실행됩니다.

페코를 사용한 프로세스 탐색
페코를 사용한 프로세스 탐색

페코 인터페이스에서 크롬Chorme 프로세스들을 검색해봅니다.

페코로 ps 결과에서 chrome을 검색한 결과
페코로 ps 결과에서 chrome을 검색한 결과

Ctrl + Space로 크롬과 관련된 프로세스들을 선택합니다.

페코에서 크롬 프로세스들을 일괄 선택한다
페코에서 크롬 프로세스들을 일괄 선택한다

다중 선택이 된 상태에서 엔터를 입력합니다. 페코 명령어는 위에서 선택한 결과를 파이프를 통해 awk에 넘겨줍니다. awk에는 페코의 출력결과를 입력으로 받아 2번째 컬럼의 값인 프로세스ID만을 출력해줍니다.

다중 선택한 결과를 aws에 넘겨서 프로세스 ID를 출력한 결과
다중 선택한 결과를 aws에 넘겨서 프로세스 ID를 출력한 결과

이 아이디 값들에 해당하는 프로세스들을 바로 종료할 수는 없을까요? awk로 뽑아낸 프로세스 아이디를 다시 xargs 명령어를 통해서 kill의 인자로 넘겨주면 한꺼번에 종료할 수 있습니다.

$ ps -ef | peco | awk '{ print $2 }' | xargs kill

이 명령어를 사용하면 페코에서 선택한 프로세스들이 한꺼번에 종료됩니다.

페코를 사용한 명령어 히스토리 검색

셸에는 숨겨진 치트키가 하나 있습니다. 바로 Ctrl + R(reverse-i-search)입니다. 셸에서 이 단축키를 입력해보시기 바랍니다. 이전에 입력했던 명령어를 자동완성해주는 기능이 실행됩니다. 셸에 익숙하지 않은 사람들이 이 기능을 접하고 놀라곤합니다. 이 기능은 매우 편리합니다만, 여기에 페코를 끼얹으면 비교할 수 없을만큼 편리하게 이전에 입력한 명령어들을 탐색할 수 있습니다.

맥OS의 Zsh에서 페코로 명령어 히스토리 검색하고 실행하기

명령어 히스토리와 페코를 연동하려면 약간의 셸스크립트가 필요합니다. 여기서는 uchiko 님의 스크립트를 사용해서 셋업해보겠습니다. 이 스크립트는 Zshoh-my-zsh을 지원합니다. 다음 스크립트를 source에 넘겨주면 작동합니다.*

* 스크립트를 source로 로드한 경우 현재 실행중인 셸에서만 스크립트가 적용됩니다. 셸을 실행할 때 항상 이 스크립트가 적용되도록 하려면 추가 작업이 필요합니다. 아래 스크립트를 적당한 위치(예 - ~/.zsh/peco-history.zsh)에 파일로 저장한 다음 ~/.zshrc 파일 마지막 줄에 source ~/.zsh/peco-history.zsh을 추가해줍니다. 이제 셸을 실행할 때마다 스크립트의 내용이 적용됩니다.

# from http://qiita.com/uchiko/items/f6b1528d7362c9310da0 by uchiko

function peco-select-history() {
    local tac
    if which tac > /dev/null; then
        tac="tac"
    else
        tac="tail -r"
    fi
    BUFFER=$(\history -n 1 | \
        eval $tac | \
        peco --query "$LBUFFER")
    CURSOR=$#BUFFER
    zle clear-screen
}
zle -N peco-select-history
bindkey '^r' peco-select-history

이 파일을 적당한 위치에 저장하고 source로 읽어옵니다. Ctrl + R을 누르면 명령어 히스토리를 필터링할 수 있는 페코가 실행됩니다. 이 글을 작성하면서 입력했던 명령어들이 보입니다.

Ctrl + R을 키를 입력하면 페코가 실행되고 이전에 실행한 명령어들이 출력된다
Ctrl + R을 키를 입력하면 페코가 실행되고 이전에 실행한 명령어들이 출력된다

저는 기억력이 좋지 않아서 docker와 같이 긴 명령어는 잘 못 외웁니다. 페코를 사용해 기억을 더듬어 이전에 실행했던 redis 이미지 실행 명령어를 증분검색해봅니다.*

* 이 명령어는 제가 이전에 실행을 했던 명령어들입니다. 따라서 사람마다 결과가 다릅니다. 페코를 통해서 직접 실행했던 명령어들을 검색해보세요.

docker와 redis 조합으로 이전에 실행했던 명령어를 탐색한 결과
docker와 redis 조합으로 이전에 실행했던 명령어를 탐색한 결과

빙고! 이걸 선택하면 이 명령어를 바로 실행할 수 있습니다.

페코에서 찾은 명령어로 레디스 컨테이너를 실행한 결과
페코에서 찾은 명령어로 레디스 컨테이너를 실행한 결과

멋지지 않나요?

맥OS, Bash에서 명령어 히스토리 검색하고 실행하기

배시Bash에서는 yungsang 님의 스크립트를 사용할 수 있습니다.

# from http://qiita.com/yungsang/items/09890a06d204bf398eea by yungsang

export HISTCONTROL="ignoredups"
peco-history() {
  local NUM=$(history | wc -l)
  local FIRST=$((-1*(NUM-1)))

  if [ $FIRST -eq 0 ] ; then
    # Remove the last entry, "peco-history"
    history -d $((HISTCMD-1))
    echo "No history" >&2
    return
  fi

  local CMD=$(fc -l $FIRST | sort -k 2 -k 1nr | uniq -f 1 | sort -nr | sed -E 's/^[0-9]+[[:blank:]]+//' | peco | head -n 1)

  if [ -n "$CMD" ] ; then
    # Replace the last entry, "peco-history", with $CMD
    history -s $CMD

    if type osascript > /dev/null 2>&1 ; then
      # Send UP keystroke to console
      (osascript -e 'tell application "System Events" to keystroke (ASCII character 30)' &)
    fi

    # Uncomment below to execute it here directly
    # echo $CMD >&2
    # eval $CMD
  else
    # Remove the last entry, "peco-history"
    history -d $((HISTCMD-1))
  fi
}
bind '"\C-r":"peco-history\n"'

셸 히스토리 설정에 대한 작은 팁

페코와 명령어 히스토리의 조합은 명령어 암기보다 느슨한 연상을 통한 점진적 학습에 도움이 됩니다. 여러 명령어를 활용하고, 시도하고, 공부하는 동안에 명령어나 옵션을 완전히 외우지 않더라도 증분검색을 통해서 내가 입력했던 명령어들을 찾아갈 수 있게 도와줍니다. 하지만 이 방법을 사용하기 해야할 일이 하나 있습니다. 바로 셸 히스토리 저장 한도를 늘리는 일입니다.

Zsh에서는 ~/.zshrc에 다음 두 행을 추가합니다.

HISTSIZE=100000000
SAVEHIST=100000000

배시에서는 ~/.bashrc에 다음 두 행을 추가합니다

export HISTFILESIZE=
export HISTSIZE=

결론

개인적으로 증분검색 인터페이스를 정말 좋아합니다. 파이어폭스의 검색 기능은 충격적으로 편리했던 기억이 납니다. 맥OS의 스팟라이트를 비롯한 다양한 런처들, 그리고 이맥스Emacs의 명령어 인터페이스 확장(anything, helm)들은 한 번 써보면 절대로 버릴 수 없습니다. 그 이유는 이러한 인터페이스가 정확한 기억에 의존하기보다 지속적인 학습을 통한 느슨한 연상을 활용하기 때문입니다. 페코는 증분검색 인터페이스를 셸 위에서 사용할 수 있도록 구현한 도구입니다.

이 글에서는 페코의 기본적인 사용법만을 소개했습니다. 그럼에도 그 강력함이 충분히 전달될 것입니다. peco/peco에 가보면 더욱 다양한 예제와 기능들에 대한 설명을 찾아볼 수 있습니다. 페코는 기본적으로 표준 입출력을 활용한 도구이기 때문에 파이프라는 셸의 철학에도 잘 부합하고 입출력을 주고받는 곳에서라면 어디에서라도 활용가능합니다.

셸 위에서도 증분 검색의 매력에 푹 빠져보시기를 바랍니다!