Modern Unix — Part 2

[들어가며]

지난 시간에는 exa(ls), bat(cat), delta(diff), dust(du), duf(df)를 소개해 드렸습니다. 앞의 도구들은 단일 실행으로 비교적 간단하게 사용할 수 있는 도구들인데요. 오늘 소개해 드리려는 도구들은 보통 다른 도구들과 파이프로 결합하여 사용할 때 더 유용한 도구들입니다.

[fd]

원하는 파일을 찾을 때 사용하는 find 는 자주 사용하게 되는 명령어 중에 하나인데요. 보통 파일을 찾을 때, 파일 명을 기준으로 찾는 경우가 많다 보니, 검색 조건에 반복적인 -name 옵션이나 -iname (대소문자 무시) 옵션을 붙이는 경우가 많습니다. fd 는 기본으로 -iname 옵션을 붙이지 않아도 되는데요. vim을 써보셨으면 아실만한 smart case를 기본으로 지원합니다.

  • 직관적인 구문: find -iname '*PATTERN*' 대신 fd PATTERN
  • 정규식(기본값) 및 glob 기반 패턴
  • 병렬화된 디렉토리 탐색으로 매우 빠릅니다.
  • 색상을 사용해 다양한 파일 유형을 강조 표시합니다(ls와 동일).
  • 병렬 명령 실행 지원
  • 스마트 케이스: 검색은 기본적으로 대소문자를 구분하지 않습니다. 패턴에 대문자가 포함되어 있으면 대소문자를 구분하는 것으로 전환됩니다.
  • 기본적으로 숨겨진 디렉토리와 파일을 무시합니다.
  • 기본적으로 .gitignore의 패턴을 무시합니다.
  • fdfind보다 50% 짧습니다 :-)

링크: https://github.com/sharkdp/fd

[ripgrep]

여러분은 어떤 도구를 가장 유용하게 사용하고 계신가요? 저는 Unix를 매우 강력하게 활용할 수 있게 해주는 도구가 바로 grep이라고 생각하는데요. 여러분들 생각은 어떠신가요? 물론 grep 자체로도 이미 훌륭하지만. grep보다 더 빠르며, 여러 가지 편의 기능을 추가한 ripgrep을 소개해 드리겠습니다. 참고로 명령어는 ripgrep이 아니라 rg로 입력합니다.

  • grep이 지원하는 대부분의 기능을 모두 지원: 검색 결과의 전후 문맥 표시, 여러 패턴 검색, 매치 결과 하이라이팅, 유니코드 지원 (grep 과 다르게 유니코드 지원이 항상 켜져 있으며, 켜져 있어도 빠른 속도를 유지)
  • 기본적으로 재귀 검색 수행 (서브 디렉터리)
  • 기본적으로 숨김 폴더 및 파일은 무시
  • 기본적으로 .gitignore에서 설정한 내용은 무시
  • 특정 타입의 파일 지정/제외 가능: rg -tpy pattern 파이썬 파일에서만 검색, rg -Tjs pattern 자바스크립트 파일을 검색에서 제외
  • 정규 표현식 엔진을 PCRE2로 변경함으로써 전후방탐색(look-around) 및 역참조(backreference) 지원
  • 매칭된 패턴의 기초적인 바꾸기(replace) 기능 제공
  • UTF-8뿐 아니라 UTF-16, latin-1, GBK, EUC-JP, Shift-JIS등 다양한 텍스트 인코딩 탐색 지원
  • 일반적인 형식(brotli, bzip2, gzip, lz4, lzma, xz, 또는 zstandard)으로 압축된 파일 검색을 지원
  • PDF 텍스트 추출, 마이너 한 압축 포맷의 해제, 복호화, 자동 인코딩 감지 등 임의의 입력 전처리 필터를 지원
  • 설정 파일을 통해 설정 가능

이 중에 특정 파일 타입을 제외하는 기능이 매우 유용합니다. minified된 스크립트는 보통 한 줄로 되어 있어서 매칭이 쉽게 될뿐더러, 출력을 어지럽히니까요. .gitignore에 지정해둔 파일을 검색하지 않는 것 또한 현대식 도구가 가진 특징이라 볼 수 있겠네요.

링크: https://github.com/BurntSushi/ripgrep

[fzf]

리스트에서 하나의 아이템만 선택하려면 어떻게 할까요? 보통 grep, head, tail 등을 이용하실 텐데요. 더 직관적이고 빠른 방법으로 이 작업을 할 수 있는 도구가 있습니다. fzf는 표준 입력으로 줄바꿈으로 구분된 리스트를 전달받아, 사용자의 실시간 피드백을 받은 후, 선택된 항목을 표준 출력으로 표시합니다. fzf로 파일 리스트, 커맨드 히스토리, 프로세스, git 커밋 등 모든 리스트에서 원하는 항목을 즉각적으로 찾을 수 있습니다.

  • 휴대성(정적 링크된 단일 바이너리), 의존성 없음
  • 빠른 속도
  • 종합적인 기능 세트
  • 화면 크기에 따른 유연한 레이아웃
  • Vim/Neovim 플러그인, 키 바인딩 및 퍼지 자동 완성 기능

fzf를 아무런 인자 없이 실행하면, 현재 및 하위 디렉터리에 있는 파일 목록이 입력이 됩니다(숨겨진 파일 제외). 다음 명령을 실행하여 어떤 일이 벌어지는지 확인해 보세요.

vim $(fzf)

또한 이전에 소개해 드린 bat와 결합하면, 파일을 선택하기 전에 미리 보면서 선택할 수도 있습니다.

fzf --preview 'bat -f {}'

참고로 이 도구는 vim-plug의 제작자분께서 만드셨는데요. 한국 분이라고 합니다! 펄럭~🇰🇷 👏👏👏

링크: https://github.com/junegunn/fzf

[McFly]

이전에 입력했던 명령어를 다시 입력해야 할 때 보통 Ctrl+R(명령 줄 히스토리 검색)로 확인을 하죠. 이제 이 작업마저도 현대식으로 대체되었습니다. mcfly라는 도구는 단순히 과거의 명령어를 순차적으로 보여주는 것이 아니라, 현재 작업 디렉터리 및 명령어 순서, 과거 종료 코드 등 여러가지 상황들을 고려하여, 커맨드 히스토리에서 실시간으로 우선순위가 정해져서 사용자에게 제안합니다.

  • ctrl-r을 다시 바인딩 하여 소규모 신경망으로 우선순위를 지정한 전체 화면 역방향 기록 검색을 불러옵니다.
  • 셸 히스토리를 강화하여 명령 종료 상태, 타임스탬프, 실행 디렉터리를 SQLite 데이터베이스에서 추적합니다.
  • 일반 셸 히스토리 파일도 유지하므로 원할 때 언제든지 McFly 사용을 중지할 수 있습니다.
  • 전체 유니코드 지원.
  • McFly 데이터베이스와 셸 히스토리 파일에서 모든 히스토리 항목을 스크러빙하는 간단한 작업이 포함되어 있습니다.
  • 향후 다른 셸에도 확장할 수 있도록 설계되었습니다.
  • Rust로 작성되어 빠르고 안전합니다.
  • 검색할 때 %를 입력하여 원하는 수의 문자와 일치 시킬 수 있습니다.

McFly의 핵심은 결국 우선순위를 지정하는 알고리즘에 있는데요. 사용자의 의도를 정확히 파악하여 사용자가 원하는 명령이 가장 먼저 제안 되도록 하는 것이 이 도구의 목표라고 합니다. 명령을 제안할 때 McFly는 다음 사항을 고려합니다:

  • 명령을 실행한 디렉토리. 나중에 같은 디렉토리에서 해당 명령을 실행할 가능성이 높기 때문입니다.
  • 명령 전에 입력한 명령(예: 명령의 실행 컨텍스트).
  • 명령을 실행하는 빈도.
  • 마지막으로 명령을 실행한 시간.
  • 이전에 맥플라이에서 해당 명령을 선택한 적이 있는지 여부.
  • 명령의 과거 종료 상태. 이전에 실패한 명령은 실행하고 싶지 않을 것입니다.

bash를 사용 중이라면 mcfly를 설치한 뒤, ~/.bashrc에 아래와 같은 내용을 추가합니다.

eval "$(mcfly init bash)"

재로그인하거나, 현재 세션에서 . ~/.bashrc 를 입력하여 사용합니다. ctrl + r 을 입력하면 mcfly 의 검색창이 독립된 화면으로 실행됩니다. 독립된 창에서 실행되는 것은 개인적으로 아쉬운 부분이네요.

링크: https://github.com/cantino/mcfly

[choose]

테이블 형태의 데이터를 다루기 위해 cut이나 awk를 많이 사용하시죠? 하지만 아쉬운 점들이 있는데요. cut은 연속되어 있는 구분자를 다루지 못한다는 점이고, awk는 태생적 한계로 인한 느린 속도와 긴 명령을 단점으로 꼽을 수 있겠습니다. choose는 바로 이러한 두 도구의 가려운 부분을 해소해 줄 수 있는 도구입니다.

  • 파이썬의 리스트 슬라이스와 유사한 간결한 필드 선택 구문
  • 줄 끝에서 음수 인덱싱
  • 선택적 시작/끝 인덱스
  • 제로 인덱싱
  • 역 범위
  • 충분히 긴 입력의 경우 cut보다 약간 빠르며, awk보다 훨씬 빠름
  • Rust의 정규식 구문을 사용하는 정규식 필드 구분 기호
choose 5                # print the 5th item from a line (zero indexed)

choose -f ':' 0 3 5 # print the 0th, 3rd, and 5th item from a line, where
# items are separated by ':' instead of whitespace

choose 2:5 # print everything from the 2nd to 5th item on the line,
# inclusive of the 5th

choose -x 2:5 # print everything from the 2nd to 5th item on the line,
# exclusive of the 5th

choose :3 # print the beginning of the line to the 3rd item

choose -x :3 # print the beginning of the line to the 3rd item,
# exclusive

choose 3: # print the third item to the end of the line

choose -1 # print the last item from a line

choose -3:-1 # print the last three items from a line

명령어가 살짝 길다는 단점이 있지만, 그래도 더 이상 cutawk 사이에서 고민할 필요는 없겠네요.

링크: https://github.com/theryangeary/choose

[sd]

sdsed의 대체 도구입니다. sed는 매우 강력하지만, 간단한 치환 작업을 하기 위해서도 항상 명령 구문을 지켜주어야 합니다. 구문이 복잡해질수록 뭘 하려는 건지 알아보기 힘들며, 가독성도 안 좋아지죠. 또한, 정규 표현식의 특수문자를 escape 하는 규칙도 직관적이지 않습니다. 그룹 캡처 특수 문자인 (...)\(...\) 와 같이 escape 해야 하지만, 문자셋 특수 문자인 [...] 는 그대로 써야하며, 특정 횟수 반복 문자인 {...}\{...\} 와 같이 써야 합니다. 익숙해지면 상관없겠지만, 일관적이지는 않다는 것을 알 수 있는데요. sed의 간단한 버전인 sd를 사용하면 이러한 고민을 할 필요가 없습니다.

  • 간편한 정규식: sd는 JavaScript와 Python에서 이미 알고 있는 정규식 구문을 사용합니다. 이제 sed나 어색한 구문으로 고민할 필요 없이 즉시 생산성을 높일 수 있습니다.
  • 문자열 리터럴 모드: 비 정규식 찾기 및 바꾸기. 더 이상 백슬래시나 이스케이프 해야 하는 특수 문자를 기억할 필요가 없습니다.
  • 읽기 쉽고 쓰기 쉬움: 찾기 및 바꾸기 표현식이 분할되어 있어 읽고 쓰기 쉽습니다. 더 이상 닫히지 않은 슬래시와 이스케이프 슬래시를 혼동하지 않아도 됩니다.
  • 스마트하고 상식적인 기본값: 기본값은 상식을 따르며 일반적인 일상 사용에 맞게 조정되어 있습니다.

sed는 훨씬 더 많은 일을 하지만, sd는 한 가지 일을 잘하는 데 집중합니다.

sd가 빛을 발하는 몇 가지 예시를 소개합니다:

  • 모든 항목을 대체하는 더 간단한 구문:
    – sd: sd before after
    – sed: sed s/before/after/g
  • 개행을 쉼표로 바꾸기:
    – sd: sd '\n' ','
    – sed: sed ':a;N;$!ba;s/\n/,/g'
  • 슬래시가 포함된 문자열에서 내용 추출하기:
    – sd: echo "sample with /path/" | sd '.*(/.*/)' '$1'
    – sed: 표현식에 따라 매번 다른 구분 기호를 사용
    * echo "sample with /path/" | sed -E 's/.*(\\/.*\\/)/\1/g'
    * echo "sample with /path/" | sed -E 's|.*(/.*/)|\1|g'
  • 파일을 제자리에서 수정하기:
    – sd: sd before after file.txt
    – sed: sed -i -e 's/before/after/g' file.txt — 표현식에 -e 를 사용하여 -i 옵션의 백업 확장자로 인식하지 않도록 하기

링크: https://github.com/chmln/sd

[cheat]

특정 명령어가 낯설거나 사용법이 익숙치 않다면, 주로 --helpman 페이지로 옵션을 확인하죠. 하지만, 둘 다 실용적인 예제가 아니기 때문에 오히려 구글링을 먼저 하시는 분들도 많으실 것 같습니다. cheat 는 바로 이런 상황에서 사용하는 도구인데요. 자주 사용하는 명령어 형식만 추려서 보여주기 때문에, 사용법을 훨씬 쉽게 확인할 수 있습니다.

다만, cheat에도 원하는 사용법이 나오지 않는다면, --helpman 페이지를 통해 사용법을 터득해야 합니다. 설명이 영어로 되어 있어, 조금 주의 깊게 읽어봐야 하는 점도 단점이긴 하지만, 어서 자국어화가 되어 한국인에게도 쓰기 편한 날이 오면 좋겠네요.

링크: https://github.com/cheat/cheat

[bottom]

top은 전체적인 시스템 상황을 한눈에 볼 수 있는 모니터링 도구입니다. 하지만 텍스트로만 이루어져 있어, 이상 상황을 한눈에 감지하기 어렵기도 합니다. 그래서 요즘 모니터링 도구들은 시각적인 효과를 조금 더 강조하는 추세입니다. 이쪽 계열에서 유명한 도구들로는 htop , gtop 등이 있습니다. bottom도 이 중 하나인데요. 모니터링 도구로 한번 활용해 보는 것도 나쁘지 않을 것 같습니다. 명령어는 bottom이 아니라 btm으로 입력합니다.

다음은 자주 사용하는 키 바인딩입니다.

  • ? : 도움말
  • q , ctrl + c : 프로그램 종료
  • esc : 취소
  • f : 업데이트 일시정지 / 재개
  • e : 선택된 위젯 확대 / 원래대로
  • ctrl + 방향키, shift + 방향키, WASD , HJKL : 위젯 선택
  • 방향키, hjkl : 위젯 내 항목 선택

링크: https://github.com/ClementTsang/bottom

[procs]

위에서 소개해 드린 bottomtop을 대체하기 위한 도구였죠. top이 모니터링 도구라면, 특정 프로세스 정보를 자세히 보기 위한 명령으로는 ps가 있습니다. procsps에서 색상 출력 기능이 강화되고, 추가적인 정보를 제공하는 도구입니다. 또한 ps와는 다르게 아무런 인자 없이 실행해도 모든 프로세스를 출력합니다.

  • 인간 친화적인 컬러 출력
    – 터미널 배경에 따른 자동 테마 감지
  • 멀티-컬럼 키워드 검색
  • ps에서는 지원하지 않는 몇 가지 추가 정보
    – TCP/UDP 포트
    – 읽기/쓰기 처리량
    – docker 컨테이너 이름
    – 추가 메모리 정보
  • pager 지원
  • 감시 모드 (top과 같은)
  • 트리 보기

링크: https://github.com/dalance/procs

[zoxide]

마지막으로 소개해 드릴 도구는 zoxide입니다. 이름만 봐서는 어떤 명령을 대체하려고 하는지 감이 안 오는데요. 바로 cd 명령입니다. 이전에 McFly에서 보셨던 것과 같이 자주 이동하는 디렉터리나 최근에 이동했던 디렉터리를 기억하여 간단한 키워드 만으로도 디렉터리를 이동할 수 있습니다. 후보군에서 가려내기 위한 점수 계산은 frecency로 이루어진다고 하는데, frecency란 빈도(frequency), 최신성(recency)을 결합한 휴리스틱 지표 값으로 Mozilla Firefox 브라우저의 개발을 위해 만들어진 지표라고 합니다.

z foo      # foo와 일치하는 가장 높은 순위의 디렉터리로 이동합니다.
z foo bar # foo와 bar가 일치하는 가장 높은 순위의 디렉터리로 이동합니다.
z foo / # foo로 시작하는 하위 디렉터리로 이동합니다.

z ~/foo # z도 일반 cd 명령처럼 작동합니다.
z foo/ # 상대 경로로 이동
z .. # 한 단계 위로 이동
z - # 이전 디렉터리로 이동

zi foo # 대화형 선택으로 cd (fzf 사용)

z foo<SPACE><TAB> # 대화형 자동완성 표시(zoxide v0.8.0+, bash 4.4+/fish/zsh만 해당)

zoxide는 간단하고 예측 가능한 알고리즘을 사용하여 쿼리를 해결합니다:

  1. 모든 일치는 대소문자를 구분하지 않습니다.
    - z foo/foo뿐만 아니라 /FOO와도 일치합니다.
  2. 경로 내에 모든 용어(슬래시 포함)가 순서대로 존재해야 합니다.
    - z fo ba/foo/bar와 일치하지만 /bar/foo와는 일치하지 않습니다.
    - z fo / ba/foo/bar와 일치하지만 /foobar와는 일치하지 않습니다.
  3. 마지막 키워드의 마지막 구성 요소는 경로의 마지막 구성 요소와 일치해야 합니다.
    - z bar/foo/bar와 일치하지만 /bar/foo와 일치하지 않습니다.
    - z foo/bar(마지막 구성 요소: bar)는 /foo/bar와 일치하지만 /foo/bar/baz와는 일치하지 않습니다.
  4. 일치 항목은 빈도 내림차순으로 반환됩니다.

링크: https://github.com/ajeetdsouza/zoxide

[기타]

여기에 더 많은 도구들을 소개해 드리고 싶지만, 더 길어지면 루즈해질 것 같아 여러분들이 관심 있으실 만한, 다른 프로젝트들을 짧게 소개해 드리겠습니다.

— — —

[lsd]

아이콘을 지원하며, 설정 파일을 통해 다양한 커스터마이징이 가능한 ls

링크: https://github.com/Peltoche/lsd

— — —

[jq]

셸로 JSON을 자유자재로 다룰 수 있다고? JSON 데이터의 필수 도구

링크: https://github.com/stedolan/jq

— — —

[hyperfine]

단 하나의 느려짐도 용납할 수 없다! 커맨드라인 벤치마크 도구.

링크: https://github.com/sharkdp/hyperfine

— — —

[gping]

ping 응답 속도를 그래프로 확인해보자.

링크: https://github.com/orf/gping

— — —

[httpie]

curl로 다양한 HTTP 요청을 보내는 것에 지쳤다면?

링크: https://github.com/httpie/httpie

[마치며]

Part 1에 이어 Part 2도 소개해 드릴 수 있게 되어 좋았습니다.

여러분은 개발자에게 있어 가장 중요한 덕목이 무엇이라 생각하시나요?

물론 여러 가지 항목들이 있을 수 있겠지만, 제가 가장 중요하게 생각하는 것은 “불편한 것을 참지 못하는 자세”입니다.

이러한 도구들도 결국 기존의 Unix 도구들에서 느꼈던 불편한 점들을 개선하려는 프로젝트들이죠.

어느 누구도 불편을 느끼지 않는다면, 개선도 없고, 발전도 없을 것입니다.

이러한 컴퓨터 공학의 세계에서 여러분들이 느낀 불편을 개선하여 모두에게 공유해보는게 어떨까요?

정말 멋진 일인 것 같습니다.

--

--

펜타시큐리티 보안기술연구소
PentaSecurity Labs

펜타시큐리티 보안 기술 연구소 사람들의 생활과 기술 연구 및 각종 활동에 관한 이야기를 담은 블로그