자주 쓰는 명령어를 하나씩 — 설명, 사용법, 실제 결과로 정리한다. 현대 도구는 대체하는 고전 명령을 같이 적었다. 아래 출력은 전부 WSL에서 직접 실행한 것이고, 현대 도구는 정적 바이너리 하나라 릴리스에서 받아 ~/.local/bin에 두면 된다.

고전 명령을 대체하는 현대 도구

ezals 대체

디렉터리를 트리로 보여주고 색·git 상태까지 붙는다. 옛 exa는 유지보수가 끊겨(README 첫 줄이 “unmaintained, use eza”) 커뮤니티 fork인 eza로 넘어왔다. 명령은 거의 호환된다.

$ eza --tree --level=2 demo
demo
├── big
   └── f
├── logs
   ├── app.log
   └── old.log
└── src
    └── main.rs

ripgrep (rg)grep 대체

기본으로 .gitignore를 존중해 node_modules·빌드 산출물을 알아서 건너뛰고, 재귀·줄번호가 기본이라 grep -rn을 칠 필요가 없다.

$ rg -n 'TODO' demo
demo/src/main.rs:2:    // TODO: 검증

fdfind 대체

find보다 문법이 단순하고 빠르다. .gitignore를 존중하고, -e로 확장자를 거른다.

$ fd -e log . demo
demo/logs/app.log
demo/logs/old.log

우분투/데비안에선 이미 다른 패키지가 fd를 써서 실행 파일이 fdfind로 깔린다(패키지명은 fd-find). ln -s $(which fdfind) ~/.local/bin/fd로 통일한다.

batcat 대체

문법 강조, 줄번호, git 변경 표시, 페이저가 붙는다. 긴 코드·설정을 훑을 때.

$ bat --style=numbers,header demo/src/main.rs
     File: demo/src/main.rs
   1 fn main() {
   2     // TODO: 검증
   3     let x = 1;
   4 }

터미널에선 여기에 문법 색까지 붙는다. 구버전 우분투/데비안에선 실행 파일이 batcat으로 깔리니 ln -s /usr/bin/batcat ~/.local/bin/bat.

jq — JSON 다루기

셸에서 JSON을 grep·sed로 씨름하지 말고 jq로 자른다. .a.b로 파고들고 []로 배열을 펼친다.

$ echo '{"user":{"name":"kim","roles":["dev","admin"]}}' | jq -r '.user.name'
kim
$ echo '{"user":{"name":"kim","roles":["dev","admin"]}}' | jq -r '.user.roles[]'
dev
admin

length·select() 같은 필터가 붙어, API 응답이나 설정 JSON을 훑을 때 사실상 필수다.

dustdu 대체

어느 디렉터리가 용량을 먹는지 트리로 보여준다. du -sh * | sort -h를 손으로 조합할 필요가 없다.

$ dust -d1 -b demo
 8.0K   ┌── src
 12K   ├── logs
6.0M   ├── big
6.0M ┌─┴ demo

-d1은 한 단계 깊이만, -b는 사용률 막대를 끈다(안 끄면 각 줄에 색 막대가 붙는다).

sdsed 대체

찾아 바꾸기가 훨씬 직관적이다. 정규식이 기본이고, 캡처 그룹은 ${1}처럼 쓴다.

$ echo 'hello world' | sd 'world' 'sd!'
hello sd!
$ echo '2026-08-11' | sd '(\d+)-(\d+)-(\d+)' '${3}/${2}/${1}'
11/08/2026

seds/.../.../g와 이스케이프 지옥 없이 읽힌다. 다만 스크립트에는 여전히 sed를 쓴다(아래 원칙 참고).

dufdf 대체

디스크 사용량을 표로 깔끔하게. --only local을 붙이면 네트워크·가상 마운트를 뺀다.

$ duf /
╭────────────────────────────────────────────────────────────────────────────╮
 1 local device
├────────────┬─────────┬───────┬────────┬──────────────────┬──────┬────────────┤
 MOUNTED ON    SIZE  USED  AVAIL       USE% TYPE FILESYSTEM
├────────────┼─────────┼───────┼────────┼──────────────────┼──────┼────────────┤
 / 1006.9G 15.4G 940.3G             1.5% ext4 /dev/sdd
╰────────────┴─────────┴───────┴────────┴──────────────────┴──────┴────────────╯

USE% 칸은 터미널에서 색 막대로 그려진다.

choosecut/awk의 열 뽑기

0부터 세는 인덱스로 열을 고른다. -f로 구분자를 바꾸고, 뒤에서부터 세는 음수 인덱스도 된다.

$ echo 'alpha beta gamma delta' | choose 1 3
beta delta
$ echo 'root:x:0:0:root:/root:/bin/bash' | choose -f ':' 0 6
root /bin/bash

awk '{print $2,$4}'보다 짧다. 열을 넘어 합계·조건 계산이 필요하면 그땐 awk.

고전 명령 — find로 오래된 로그 지우기

find는 어디에나 있어 스크립트에도 쓴다. 기본 사용법과 결과부터:

$ find demo/logs -name '*.log' -mtime +10 -type f
demo/logs/old.log

다만 예전 메모의 이 한 줄엔 함정이 셋 있었다.

find -name '*.log' -mtime +10 | xargs rm

1) 공백이 있는 파일명에서 깨진다. xargs는 공백을 구분자로 본다. b with space.log 하나가 인자 세 개로 쪼개진다.

$ find . -name '*.log' -mtime +10 -type f | xargs sh -c 'echo "got $# args:"; printf "  [%s]\n" "$@"' _
got 3 args:
  [./b]
  [with]
  [space.log]

-print0 / xargs -0으로 널 구분자를 쓰거나 find-delete를 쓴다.

find . -name '*.log' -mtime +10 -type f -print0 | xargs -0 rm
find . -name '*.log' -mtime +10 -type f -delete   # 더 간단

2) -type f가 없으면 디렉터리도 걸린다. *.log라는 이름의 디렉터리까지 걸리고, rm -rf였다면 디렉터리 전체가 조용히 사라진다.

$ find . -name '*.log' -mtime +10        # -type f 없음 → 디렉터리도
./b with space.log
./c.log
$ find . -name '*.log' -mtime +10 -type f  # 파일만
./b with space.log

3) -mtime +10은 “10일 초과”가 아니다. GNU findutils 매뉴얼에 따르면 파일 나이(초)를 86400으로 나눈 뒤 소수점을 버린 정수로 비교하고, +10은 그 정수가 10보다 큰 것이다. 10.9일 파일은 정수 10으로 절삭돼 안 걸리고, 만 11일이 되어야 걸린다.

지우기 전엔 -delete-print로 바꿔 뭐가 걸리는지 먼저 본다. 상시 운영 서비스라면 매번 손으로 지우기보다 logrotate(/etc/logrotate.d/)에 회전·압축·보관 기간을 맡기는 쪽이 맞다.

두 가지 원칙

  • 스크립트엔 고전 도구. 대화형 쉘에서 편하다고 스크립트에 eza·fd·bat·sd를 넣지 않는다. 남의 서버·CI 컨테이너엔 기본으로 없다. 스크립트는 ls·find·cat·sed처럼 어디에나 있는 고전 도구로 짜고, 새 도구는 터미널에서만 쓴다.
  • 직접 배포 .deb는 이름이 그대로. 위의 batcat/fdfind 개명은 배포판 패키지 사정이다. 각 프로젝트가 직접 내는 .deb 릴리스는 실행 파일명이 bat/fd 그대로다.

참고