GNU Coreutils가 거절한 기능 요청들: "안 만드는 것"도 설계다

한눈에 보는 요약
GNU Coreutils 프로젝트는 그동안 들어온 수많은 기능 요청 중 상당수를 채택하지 않았고, 그 거절 사유와 메일링 리스트 논의를 한곳에 정리해 두었습니다. 핵심은 단순합니다.
- 기존 도구 조합으로 충분한가? →
cp --resume은rsync,du --sort는du -h | sort -h, 재귀적touch는find로 해결됩니다. - 이 도구가 담당할 계층인가? → CSV 파싱은 전용 유틸리티가, 병렬 복사는 더 낮은 계층이 맡는 편이 낫습니다.
- 표준과 안전을 해치지 않는가? →
echo의--처리는 POSIX 위반이고,rm --parents는 너무 위험합니다.
메인테이너들은 "무엇을 거절할지 정하고, 기여자에게 납득할 만한 근거를 제시하는 일"이야말로 Coreutils 개발에서 가장 어려운 작업 중 하나라고 말합니다. 거절된 아이디어들이 나빠서가 아니라, 각기 다른 이유로 이 프로젝트에 맞지 않았을 뿐입니다.
거절 사유는 크게 네 갈래
1) 이미 있는 도구로 된다
유닉스 철학의 정석입니다. 새 옵션을 추가하는 대신 파이프라인을 권합니다.
| 요청 | 대안 |
| --- | --- |
| cat --timestamp | awk, perl |
| cat --header | tail -n+1 |
| cat -S (공백 행 압축) | sed 's/^ *$//' \| cat -s |
| cut -f2,1 (필드 순서 변경) | awk, join |
| ls --octal | stat, find |
| mv --safe | cp ... && rm |
| mv --parents | cp -l --parents |
| sort --header | sed, head |
| md5sum --pipe | tee |
| touch -R | find . -exec touch -am {} + |
du --sort이 대표적입니다. 정렬은 sort -h가 이미 완벽하게 해내는 일이니, du 안에 정렬 로직을 또 넣을 이유가 없다는 판단이죠.
2) 이 계층에서 풀 문제가 아니다
- 속도 제한:
dd --limit-speed,cp --bwlimit같은 전송 속도 제어는pv,trickle,rsync같은 상위 도구의 몫입니다. - CSV 처리:
cut --csv요청이 있었지만, CSV는 따옴표·이스케이프·개행 등 예외가 많아 전용 유틸리티가 적합하다고 봤습니다. - 병렬화:
cp --parallel,md5sum --threads,join --parallel모두 거절됐습니다. 서로 다른 파일을 병렬로 처리하는 일은 UNIX 도구 조합만으로도 충분하고, 복사 병렬화는 더 낮은 계층에서 다루는 편이 낫다는 이유였습니다. - 파일시스템 특화 기능: ext2 확장 속성 복사나 OCFS2 reflink 사용량 계산처럼 특정 파일시스템에 묶인 기능은
copyfile()같은 파일시스템 독립 인터페이스가 먼저 필요합니다.
3) 표준 호환성과 안전성
여기가 가장 단호합니다.
echo에--옵션 종료 처리를 넣자는 요청 → POSIX 위반이라 불가rm --parents(트리를 거슬러 올라가며 삭제) → 지나치게 위험rm이unlink()대신remove()를 쓰게 하자는 요청 → 표준화된 동작은 바꿀 수 없음rm -d→rmdir가 같은 일을 하고 혼동도 적음
"현재 디렉터리를 통째로 지우고 싶다"는 요청에는 rm -rf "$PWD", 내부 파일만 지우려면 rm -rf * .[!.] .??* 또는 find . -delete를 안내합니다.
4) 중복 인터페이스와 옵션 비대화
cp --to:--target보다 낫더라도 같은 일을 하는 방법이 두 개일 필요는 없다cut -C:--complement를--co로 줄여 쓸 수 있으니 별칭 불필요df -g: 출력 단위마다 별도 옵션을 만드는 방식 자체를 피함 (df -h면 충분)ls의 UTF-8 화살표,cp --verbose의 유니코드 기호: 같은 이유로 거절
환경 변수와 설정 파일보다 셸을 쓰세요
눈여겨볼 만한 원칙이 하나 더 있습니다. SORT_BUFFER_SIZE 같은 새 환경 변수나 전역 설정 파일로 기본 동작을 바꾸는 방식은 가능하면 피한다는 것입니다. 여러 명령이 공유할 때는 유용할 수 있지만, 명령의 동작이 보이지 않는 상태에 좌우되면 재현성과 예측 가능성이 떨어집니다.
대신 권장하는 방법은 셸 별칭, 셸 함수, 래퍼 스크립트입니다. 설정은 각자의 dotfile에 남고, 명령 자체는 어디서 실행하든 똑같이 동작하니까요.
거절만 한 건 아닙니다
모든 요청이 그냥 닫힌 건 아닙니다.
sort --by-length는 거절했지만, info 문서에 행 길이로 정렬하는 예제를 추가했습니다.df --dereference요청에는 별도 옵션을 만드는 대신df가 항상 심볼릭 링크 대상을 참조하도록 동작 자체를 바꿨습니다.min/max명령 추가는 채택하지 않았지만, 대안으로 나온sort --range={}아이디어는 유용해 보인다고 평가했습니다.
즉 "거절"은 논의의 끝이 아니라, 더 나은 형태를 찾는 과정이기도 했습니다.
우리 프로젝트에 적용한다면
이 문서가 흥미로운 이유는 Coreutils 이야기이기 때문만은 아닙니다. 기능 요청을 받는 모든 메인테이너가 쓸 수 있는 체크리스트에 가깝습니다.
- 조합으로 되는가? 기존 기능 두세 개를 이어 붙여 해결되는 일이라면, 새 옵션은 표면적만 늘립니다.
- 우리 계층의 책임인가? 더 아래나 더 위에서 푸는 게 맞는 문제는 넘기는 게 낫습니다.
- 범용적인가? 특정 환경·특정 서비스에만 쓰이는 기능(예: Internet Archive용
md5sum --base32)은 관심을 얻기 어렵습니다. - 표준을 깨거나 위험을 키우는가? 편의보다 예측 가능성과 안전이 앞섭니다.
- 거절한다면 근거를 남겼는가? Coreutils가 거절 목록을 문서로 공개한 것 자체가 좋은 관행입니다. 같은 제안이 반복될 때 논의를 처음부터 다시 하지 않아도 되니까요.
40년 넘게 유지되는 도구 모음이 지금도 단순한 이유는, 좋은 기능을 많이 넣었기 때문이 아니라 좋아 보이는 기능을 신중하게 거절해 왔기 때문에 가깝습니다.