ls, cat, grep, find를 쳐도 리눅스·맥과 똑같이 동작한다면? Coreutils for Windows는 Rust로 다시 쓴 uutils/coreutils를 마이크로소프트가 윈도우용으로 묶어 낸 프로젝트입니다. 75개 넘는 유닉스 명령을 단 하나의 작은 바이너리(coreutils.exe)에 담고, 설치 시 명령마다 하드링크(ls.exe, cat.exe…)를 만들어 WSL 없이도 네이티브로 돌립니다. 2026년 6월 Build 2026에서 공개되자마자 트렌딩에 올랐습니다. (저장소: microsoft/coreutils · Rust · ⭐1.9k · MIT 라이선스 · 프리뷰 단계)리눅스·맥에서 터미널을 쓰면 당연하게 치는 명령들이 있습니다 — 파일을 보는 cat, 목록을 보는 ls, 복사하는 cp, 찾는 find, 검색하는 grep. 이 기본 도구 묶음을 coreutils라고 부릅니다. 문제는 윈도우에는 이게 없거나, 있어도 이름과 동작이 미묘하게 다르다는 점입니다. 그래서 같은 스크립트가 리눅스에선 돌고 윈도우에선 깨지죠.
microsoft/coreutils는 그 간극을 메웁니다. 이미 존재하던 uutils/coreutils(GNU coreutils를 Rust로 새로 구현한 오픈소스)를 가져와, 윈도우에서 잘 돌도록 손보고 findutils(find·xargs)와 grep까지 합쳐 단 하나의 실행 파일로 포장한 것입니다. 마이크로소프트가 직접 유지보수하고, winget install Microsoft.Coreutils 한 줄로 설치됩니다.
맥가이버 칼(스위스 아미 나이프)을 떠올려 보세요. 겉보기엔 칼 한 자루지만, 펼치면 가위도 되고 드라이버도 되고 병따개도 됩니다. coreutils.exe가 바로 그 칼입니다. 파일 하나일 뿐인데, ls라는 이름표를 붙여 부르면 목록 도구가 되고, cat이라 부르면 파일 출력 도구가 됩니다. 윈도우는 설치할 때 명령마다 이름표(하드링크)를 75개 넘게 붙여두고, 사용자가 cp를 치면 칼은 "아, 지금은 복사 모드구나" 하고 알아서 그 기능으로 변신합니다. 도구 75개를 따로 설치하는 게 아니라, 변신하는 칼 한 자루를 두는 셈이죠.
핵심 아이디어 두 가지만 기억하면 됩니다. 첫째, 구현은 빌려오고(uutils), 포장만 직접 한다 — 명령들의 실제 로직은 업스트림 오픈소스에 있고, 이 저장소는 윈도우용 접착제·포장·배포를 담당합니다. 둘째, 여러 명령을 한 바이너리에 담는다 — 이를 멀티콜 바이너리(multi-call binary)라 하며, 리눅스의 BusyBox가 쓰는 바로 그 기법입니다.
ls·cat·cp·mv·rm·echo·wc 같은, 터미널에서 매일 쓰는 작은 도구들의 집합입니다. 원래 리눅스의 GNU coreutils가 표준이었는데, 이걸 Rust로 새로 구현한 게 uutils입니다. "코어"라는 이름처럼, 이게 없으면 셸에서 거의 아무것도 못 합니다.ls로 불렸나, cat으로 불렸나"를 보고 해당 기능을 실행합니다. 디스크·메모리를 아끼는 임베디드 리눅스의 BusyBox가 원조입니다. "이름이 곧 명령"인 셈이죠.microsoft/coreutils가 2026년 6월 트렌딩 최상위권에 오른 데에는 분명한 계기와 배경이 있습니다. 직접적 계기는 Build 2026(마이크로소프트 개발자 컨퍼런스, 6월 2일)에서의 공식 발표입니다. "윈도우에서 75개 이상의 리눅스 명령을 네이티브로"라는 헤드라인이 나오자, 개발자 커뮤니티가 즉시 저장소로 몰렸습니다.
하지만 일회성 발표 효과만은 아닙니다. 더 큰 흐름이 깔려 있습니다. 첫째, 마이크로소프트가 시스템 도구를 Rust로 가는 신호입니다. 윈도우 커널 일부, 그리고 이번 coreutils까지 — 메모리 안전성과 성능을 동시에 잡는 Rust 채택을 대기업이 공개적으로 밀고 있다는 상징성이 큽니다. 둘째, 라이선스입니다. GNU coreutils는 GPL(파생물도 소스 공개 강제)인 반면, uutils는 MIT(제약이 거의 없음)라 기업이 제품에 자유롭게 담을 수 있습니다. 윈도우에 기본 탑재하려면 이 차이가 결정적입니다.
셋째, 개발자들이 매일 겪는 실질적 마찰을 없앱니다. 리눅스·맥·WSL·컨테이너·윈도우를 오가며 일하는 사람은 같은 명령이 환경마다 다르게 동작하는 데 지칩니다. 같은 ls, 같은 플래그, 같은 파이프라인이 어디서나 똑같이 돌면 스크립트를 "번역"할 필요가 없어집니다.
| 대안 | 방식 / 특징 | 이 프로젝트와의 차이 |
|---|---|---|
| WSL | 윈도우 안에 리눅스를 통째로 띄움 | 강력하지만 무겁고 별도 환경. coreutils는 네이티브 exe라 WSL 불필요 |
| Git Bash / MSYS2 | 유닉스 도구를 윈도우로 포팅한 묶음 | C 기반·별도 셸 환경. 이건 Rust·단일 바이너리·WinGet 배포 |
| GNU coreutils | 리눅스 표준, C로 작성, GPL | uutils는 Rust·MIT라 기업 탑재·메모리 안전에 유리 |
| PowerShell 별칭 | ls가 사실 Get-ChildItem | 플래그·출력이 유닉스와 다름. 이건 진짜 유닉스 의미를 제공 |
아닙니다. 윈도우는 본질적으로 다른 OS라 구조적 차이가 남습니다. POSIX 시그널(SIGHUP 등)이 없어 kill·timeout은 빠졌고, 파일 권한은 POSIX 비트가 아니라 ACL이라 find -perm 같은 게 다르게 돕니다. 줄바꿈도 CRLF(\r\n)라 $ 패턴 매칭이 어긋날 수 있고, /dev/null 대신 NUL을 써야 합니다. "거의 같다"이지 "완전히 같다"가 아닙니다.
이 프로젝트의 미덕은 한계를 README에 표로 정리해 둔 점입니다. 어떤 명령이 CMD·PowerShell 빌트인과 충돌하는지(✅/⚠️/🛑 범례), 무엇이 의도적으로 빠졌는지(dd·shred·chmod…), 윈도우 특유의 함정(CRLF·ACL·시그널 부재)이 무엇인지를 미리 알려줍니다. "마법처럼 다 된다"고 하지 않고 경계를 명확히 그어 신뢰를 줍니다.
이 저장소의 가장 중요한 통찰은 "코드를 거의 직접 쓰지 않는다"는 것입니다. 명령들의 실제 구현은 deps/ 폴더 안의 git 서브모듈(업스트림 프로젝트들)에 있고, 이 저장소가 직접 짜는 건 그것들을 하나로 묶는 접착 코드(glue)와 빌드 설정, 윈도우 전용 처리뿐입니다. Cargo.toml과 build.rs가 진짜 주인공이죠.
| 구성요소 | 버전 / 값 | 역할 |
|---|---|---|
| Rust | edition 2024 · rust 1.88.0+ | 전체 구현 언어. 최신 에디션·툴체인을 요구 |
| coreutils 버전 | 2026.5.29 | 날짜 기반 버전 표기 (Cargo.toml [package] version 필드). Cargo 도구 자체의 버전이 아님 |
| uucore | deps/coreutils에서 path 의존 | 모든 uutils 명령이 공유하는 공통 코어 라이브러리 |
| clap | 4.5 (wrap_help·cargo·color) | 커맨드라인 인자 파싱 — 각 명령의 --help·플래그 처리 |
| phf | 0.13 (+ phf_codegen) | 명령 → 함수 조회용 완전 해시 맵. 빌드 타임 생성 |
| 크레이트 | 역할 |
|---|---|
| windows-sys | Win32 API 직접 호출. Console(코드페이지)·Registry(레지스트리) 기능만 선택 |
| cc | 빌드 시 C 코드(deps/ntsort/sort.c, DOS sort 포팅)를 컴파일 |
| winresource | 윈도우 매니페스트·아이콘·버전 정보를 exe에 박아 넣음 |
| phf_codegen / regex | build.rs에서 Cargo.toml을 파싱해 명령 맵 코드를 생성 |
| 항목 | 종류 | 출처 · 제공 명령 |
|---|---|---|
| deps/coreutils | git 서브모듈 | uutils/coreutils 포크 — ls·cat·cp·mv·rm·wc 등 70여 개 (윈도우 패치 포함) |
| deps/findutils | git 서브모듈 | uutils/findutils — find·xargs |
| deps/grep | git 서브모듈 | uutils/grep 포크 — grep·egrep·fgrep |
| deps/ntfind | 로컬 Rust 크레이트 (서브모듈 아님) | 도스 find 명령의 통합 포팅 (유닉스 find와 별개). 독립 Cargo.toml 보유 |
| deps/ntsort | 로컬 C 파일 (서브모듈 아님) | 도스 sort 명령의 C 포팅 (sort.c 단일 파일, FFI로 연결) |
deps/coreutils는 사실 uutils 저장소를 가리키는 "북마크"로, 특정 커밋에 고정됩니다. 덕분에 업스트림 원본을 복사·붙여넣기 하지 않고도 항상 정확한 버전을 끌어올 수 있습니다. "남의 책을 베끼지 않고 책 위치만 적어두는 도서관 카드"라고 보면 됩니다.ls라는 글자 → ls를 실행하는 함수"를 이 표로 한 번에 찾습니다. 전화번호부를 실행 전에 통째로 인쇄해 두는 것과 비슷합니다.이 프로젝트는 두 시점으로 나눠 봐야 이해됩니다. 빌드 시점에 Cargo.toml의 의존성 목록이 곧 명령 등록부가 되어 조회표가 생성되고, 실행 시점에 그 표를 보고 어떤 명령으로 변신할지 결정합니다.
ls.exe와 cat.exe는 복사본이 아니라 둘 다 똑같은 coreutils.exe 실체를 가리킵니다. 디스크 공간은 1개분만 쓰고, 원본을 고치면 전부 같이 바뀝니다. 윈도우 NTFS가 지원하는 기능이라 WSL 없이도 됩니다. "한 사람에게 여러 별명을 붙이는 것"과 같죠.main()은 어떻게 "변신"을 결정하나핵심 로직은 src/main.rs 한 곳에 모여 있습니다. 초보자 입장에서 가장 신기한 "어떻게 한 파일이 여러 명령이 되는가"의 답이 여기 있습니다. 해피패스(정상 경로)만 따라가 봅시다.
// ① 콘솔을 UTF-8 모드로 바꾼다 (한글·유니코드 깨짐 방지) // 원래 코드페이지·모드는 전역 AtomicU32에 저장, 종료 시 atexit()가 복구 set_console_modes(); // atexit(restore_console_modes)로 종료 시 자동 복구 // ② 빌드 타임에 생성된 조회표를 불러온다 (명령 → 함수) let utils = util_map(); let mut args = uucore::args_os(); // ③ 내가 어떤 이름으로 불렸는지 본다 (예: ls.exe → "ls") let binary = binary_path(&mut args); let binary_as_util = name(&binary)...; // ④ 이름이 'ls' 같은 실제 명령이면 그걸로, // 'coreutils'로 불렸으면 첫 번째 인자를 명령으로 본다 match utils.get(util) { Some(&(uumain, _)) => { // 표에서 찾았다! setup_localization_or_exit(util); process::exit(uumain(...)); // 해당 명령 함수 실행 후 종료 } None => { /* 못 찾으면 에러 또는 --help 처리 */ } }
풀어 쓰면 이렇습니다. ① 먼저 콘솔 출력 인코딩을 UTF-8로 바꿉니다(윈도우 기본 코드페이지에선 유니코드가 깨지므로). 원래 코드페이지·모드 값은 전역 AtomicU32(ORIGINAL_OUTPUT_CP, ORIGINAL_OUTPUT_MODE)에 저장해 두고, C의 atexit(restore_console_modes)를 등록해 프로그램이 끝나면 원래 상태로 자동 복구합니다 — Rust의 Drop 패턴이 아닌 C 스타일 atexit 콜백으로 구현된 안전장치죠. ② 빌드 때 만들어 둔 phf 조회표를 불러옵니다. ③ 자기가 ls.exe로 불렸는지 확인합니다. ④ 그 이름을 표에서 찾아, 매칭되는 함수(ls::uumain)에 인자를 넘겨 실행하고 그 결과 코드로 종료합니다.
이 레포에서 "정상"인 패턴 몇 가지를 짚어둡니다. 별칭(alias)이 있어서 [는 test로, dir·vdir는 ls로 연결됩니다. 또 find와 sort는 이중 인격입니다 — 도스식으로 불리면 통합된 ntfind/ntsort(C 포팅)로, 유닉스식으로 불리면 uutils 구현으로 갈라집니다. 그래서 기존 윈도우 스크립트도 안 깨지고 유닉스 사용자도 익숙하게 쓸 수 있죠.
ntsort_main)를 unsafe extern "C"로 호출합니다. 이미 잘 도는 C 코드를 다시 짜지 않고 그대로 재활용하는 실전 기법입니다. "통역사를 두고 외국어 함수를 부르는 것"과 같습니다.이 프로젝트에서 가장 영리한 부분입니다. 명령 목록을 손으로 두 번 적지 않습니다. build.rs가 Cargo.toml의 의존성 줄들을 정규식으로 읽어 "이 프로젝트가 가진 명령이 무엇인지" 자동으로 알아내고, 그걸로 phf 조회표 코드(uutils_map.rs)를 생성해 OUT_DIR에 씁니다. 그 파일을 main.rs가 include!로 끌어옵니다.
// build.rs — Cargo.toml을 읽어 "uu_xxx" 패키지를 명령으로 등록 let re = Regex::new(r#"package\s*=\s*"([^"]+)""#)...; for line in manifest.lines() { // uu_ls, uu_cat … 에서 'ls','cat' 추출 if let Some(util) = package.strip_prefix("uu_") { coreutils.push((util, alias)); } } // 추출한 명령들로 phf 정렬 맵 코드를 만들어 파일로 출력 let mut phf_map = phf_codegen::OrderedMap::new(); ... fs::write(out_dir.join("uutils_map.rs"), code)
의미는 이렇습니다 — "의존성에 명령을 추가하면, 등록은 자동으로 따라온다." 새 명령을 넣고 싶으면 Cargo.toml에 한 줄 추가하면 끝이고, build.rs가 알아서 조회표에 반영합니다. 사람이 두 곳을 동기화하다 실수할 여지를 구조적으로 없앤 설계입니다.
| 위치 | 역할 |
|---|---|
| Cargo.toml | 이 프로젝트의 명령 등록부 겸 빌드 설정. 의존성 줄 하나가 명령 하나 |
| build.rs | 조회표 생성 + C 컴파일 + 리소스 임베드. 빌드 자동화의 핵심 |
| src/main.rs | 실행 진입점. 이름·인자로 명령을 골라 해당 함수로 분기 |
| src/nthelpers.rs | find·sort가 도스식 호출인지 유닉스식인지 판별 |
| deps/ (서브모듈) | 실제 명령 로직 전부. 이 저장소는 거의 손대지 않고 끌어다 씀 |
이 저장소는 "가구 조립·포장 공장"에 가깝습니다. 목재(deps의 uutils 구현)는 다른 데서 가져오고, 이 공장은 그 부품들을 한 상자(coreutils.exe)에 담고, 라벨(하드링크 이름표)을 붙이고, 윈도우라는 매장에 맞게 규격(코드페이지·매니페스트)을 맞춰 출하합니다. 가구를 처음부터 깎는 게 아니라 조립·포장·유통이 핵심 부가가치죠.
실행 파일이 자기 이름을 보고 동작을 바꾸는 기법. std::env::current_exe()·args_os()로 호출 이름을 얻고, 조회표로 분기합니다. 임베디드 리눅스의 BusyBox, 컨테이너의 단일 바이너리 도구가 다 이 방식입니다. 디스크·메모리를 아끼고 배포를 단순화하는 실전 패턴을 main.rs 한 파일에서 통째로 배울 수 있습니다.
📖 한 줄 풀이: argv[0](0번째 인자 = 프로그램 이름)이 "정체성"이 된다는 발상이 핵심입니다.
Rust의 build.rs는 컴파일 직전에 실행되는 스크립트입니다. 여기선 Cargo.toml을 파싱해 phf 조회표 소스를 자동 생성하죠. "사람이 두 군데 적다 실수하는" 문제를 코드 생성으로 없애는 메타프로그래밍의 정석입니다. phf_codegen으로 완전 해시를 컴파일 타임에 굳히는 것까지 한 번에 익힙니다.
Cargo.toml의 [profile.release]는 바이너리 다이어트 교과서입니다. 각 옵션이 주석으로 효과까지 적혀 있습니다.
# 주석에 적힌 절감 효과까지 그대로: opt-level = "s" # 크기 우선 최적화 (~25% 감소) lto = true # 링크 타임 최적화 (~14% 감소) panic = "abort" # 패닉 시 되감기 제거 (~50% 감소) strip = "symbols" # 심볼 제거 (~65% 감소) codegen-units = 1 # 코드젠 단위 1 (~2% 감소)
각 플래그가 "왜" 크기를 줄이는지(되감기 코드 제거, 디버그 심볼 분리 등) 이해하면, 내 Rust 프로젝트에도 바로 적용할 수 있습니다.
콘솔 코드페이지를 UTF-8로 바꾼 뒤 반드시 원상 복구해야 하는데, 중간에 에러로 종료돼도 빠뜨리면 안 됩니다. 실제 구현은 Rust의 Drop 패턴이 아니라 C 스타일입니다. 원래 코드페이지·모드 값을 전역 AtomicU32에 저장해 두고, atexit(restore_console_modes)로 종료 콜백을 등록합니다. 프로그램이 어떤 경로로 끝나든 등록된 콜백이 자동 호출돼 복구되는 구조입니다. RAII/Drop 트레이트를 쓰는 Rust 관용 패턴과는 다르지만, "리소스 정리를 구조적으로 보장"한다는 목적은 같습니다.
왜 kill·timeout이 빠졌고(시그널 없음), find -perm이 다르며(ACL vs POSIX 비트), /dev/null 대신 NUL을 쓰는지 — README의 "Windows caveats" 표가 OS 차이의 압축 강의입니다. 크로스플랫폼 도구를 만들 때 "무엇은 같게, 무엇은 다르게 둘지"를 결정하는 감각을 길러줍니다.
CONTRIBUTING.md의 규칙이 흥미롭습니다 — "동작 변경은 업스트림(uutils)에 먼저 반영하고, 그다음 서브모듈을 갱신하라." 윈도우 전용 접착 코드만 이 저장소에 직접 PR. 대기업이 오픈소스 원본을 존중하며 협업하는 거버넌스 모델을 실물로 볼 수 있습니다. 포크·서브모듈·업스트림 PR이라는 OSS 협업 3종 세트를 배웁니다.
| 항목 | 요구사항 |
|---|---|
| 운영체제 | 윈도우 (네이티브). WSL·가상머신 불필요 |
| 셸 | 셸 통합(PSReadLine 인용 처리)은 PowerShell 7.4+ 필요. 구버전 미지원. CMD도 동작 |
| 설치(사용자) | winget install Microsoft.Coreutils 또는 릴리스 페이지에서 다운로드 |
| 빌드 툴체인 | Rust 1.88.0+, edition 2024 지원 cargo. 크기 최적화엔 nightly의 -Zbuild-std 옵션 |
| 소스 받기 | git 서브모듈 포함 클론 필요(--recurse-submodules). C 컴파일러(ntsort용) |
| 심볼릭 링크 생성 | ln -s로 새 심링크를 만들려면 개발자 모드 또는 관리자 터미널 필요(기존 심링크 읽기는 불필요) |
| 로컬 자원 | GPU·대용량 메모리 불필요. 단일 바이너리라 디스크 부담 최소 |
ls·cp·rm·cat·echo 등은 PowerShell에 같은 이름의 별칭이 이미 있어(⚠️), PATH 순서·별칭 테이블에 따라 coreutils 버전이 안 불릴 수 있습니다. dir·more·whoami·kill·timeout은 충돌·시그널 부재로 아예 빠졌습니다(🛑). 어떤 셸에서 무엇이 충돌하는지는 README의 충돌표(CMD/PowerShell ✅⚠️🛑)를 먼저 확인하세요. 또 PowerShell의 이스케이프 문자는 백슬래시가 아니라 백틱(`)이라, find . `( ... `)처럼 써야 하는 차이도 있습니다.
WinGet으로 설치한 뒤, 전체 명령 목록을 보고 몇 개를 직접 쳐보세요. 같은 exe가 명령마다 다르게 동작하는 걸 눈으로 확인하는 게 목적입니다.
# 💡 하는 일: 설치 → 전체 명령 목록 → 몇 개 실행 winget install Microsoft.Coreutils coreutils --list # 들어있는 명령 전부 출력 ls -l # 목록 (유닉스 스타일 플래그) cat README.md # 파일 출력 echo "hello" | wc -c # 파이프라인도 그대로
안심하세요 — 그냥 명령을 실행할 뿐, 시스템을 바꾸지 않습니다. PowerShell에서 빌트인에 가려지면 coreutils ls처럼 앞에 붙여 부르면 됩니다.
멀티콜의 원리를 손으로 느껴봅니다. coreutils.exe를 다른 이름으로 복사(또는 하드링크)해서 실행해 보세요. 이름만 바꿨는데 동작이 달라지는 걸 확인할 수 있습니다.
# 설치 폴더에서 (예시) — 복사본 이름이 동작을 결정한다 copy "C:\Program Files\coreutils\coreutils.exe" myls.exe .\myls.exe -l # 'myls'는 'ls'로 끝나므로 → ls처럼 동작!
왜 myls가 ls가 되는지 main.rs의 ends_with(util) 매칭 로직과 연결지어 보면, 코드가 살아 움직이는 게 보입니다.
Cargo.toml의 의존성 한 줄(예: cat = { package = "uu_cat" ... })이 build.rs의 정규식에 걸려 어떻게 util_map()의 한 항목이 되는지 종이에 단계로 적어보세요. 그리고 main.rs의 include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"))가 그 생성 파일을 어떻게 끌어오는지 연결합니다. "코드가 코드를 만드는" 메타프로그래밍의 전 과정이 손에 잡힙니다.
서브모듈까지 클론해서 직접 빌드하면, deps/가 어떻게 끌려오고 ntsort의 C 코드가 cc로 컴파일되는지 빌드 로그로 확인됩니다.
# 서브모듈 포함 클론 후 릴리스 빌드 git clone --recurse-submodules https://github.com/microsoft/coreutils cd coreutils cargo build --release # build.rs 가 코드 생성·C 컴파일까지 수행
빌드된 바이너리 크기를 [profile.release] 옵션을 켜고 끄며 비교해 보면, 최적화 플래그의 효과를 직접 체감할 수 있습니다.
같은 명령을 윈도우(coreutils)와 WSL/리눅스에서 각각 실행해 차이를 직접 수집해 보세요 — CRLF 줄바꿈이 wc -l·grep '$'에 미치는 영향, find -perm이 ACL에서 어떻게 다른지, 경로 구분자(/ vs \)가 파이프 뒤로 어떻게 흘러가는지. README의 caveats 표를 내 손으로 재현·검증하면, 크로스플랫폼 도구의 한계를 추상이 아니라 데이터로 이해하게 됩니다.
| 주차 | 주제 | 학습 자료 / 키워드 |
|---|---|---|
| 1주 | coreutils와 uutils 생태계 | GNU coreutils 명령들, uutils/coreutils, BusyBox, GPL vs MIT 라이선스 |
| 2주 | Rust CLI 기초 | clap(인자 파싱), 멀티콜 바이너리, std::env·args_os, 종료 코드 |
| 3주 | 빌드 스크립트 & 메타프로그래밍 | build.rs, OUT_DIR·include!, phf/phf_codegen, cc 크레이트로 C 컴파일 |
| 4주 | 윈도우 시스템 프로그래밍 & 패키징 | windows-sys(Win32 Console/Registry), FFI extern "C", atexit+전역 AtomicU32(콘솔 복구 패턴), winresource, WinGet 매니페스트, 바이너리 크기 최적화 |
이 4주는 "명령어를 쓰는 사람"에서 "명령어를 만드는 사람"으로 넘어가는 다리입니다. coreutils는 규모가 작아 전체를 통독할 수 있으면서도, Rust 시스템 프로그래밍의 핵심 도구(clap·build.rs·phf·FFI·windows-sys)가 한 곳에 모인 드문 교재입니다. 큰 프레임워크를 헤매기 전에, "제대로 만든 작은 도구 하나"를 끝까지 읽는 경험이 실력을 키웁니다.
| 용어 | 의미 |
|---|---|
| coreutils | OS의 핵심 기본 명령어 묶음(ls·cat·cp…). 원조는 리눅스의 GNU coreutils |
| uutils | GNU coreutils를 Rust로 다시 구현한 크로스플랫폼 프로젝트(MIT). 이 저장소의 실구현 출처 |
| 멀티콜 바이너리 | 한 exe가 호출된 이름에 따라 다른 명령처럼 동작하는 방식(BusyBox 패턴) |
| 하드링크 | 같은 파일에 여러 이름표를 다는 것. ls.exe·cat.exe가 모두 coreutils.exe를 가리킴 |
| uucore | 모든 uutils 명령이 공유하는 공통 코어 라이브러리(출력·에러·로케일 등) |
| clap | Rust의 대표 커맨드라인 인자 파서. 각 명령의 플래그·--help 담당 |
| phf | 키가 고정일 때 충돌 없이 즉시 찾는 완전 해시. 명령→함수 조회표에 사용 |
| build.rs | 컴파일 직전 실행되는 Rust 빌드 스크립트. 여기선 조회표 생성·C 컴파일 수행 |
| FFI | 다른 언어 함수를 직접 부르는 다리. Rust→C(ntsort) 호출에 사용 |
| atexit + AtomicU32 | 콘솔 코드페이지 복구에 사용된 실제 패턴. 원본값을 전역 원자변수에 저장 후 atexit() 콜백으로 종료 시 자동 복구 (Rust Drop 패턴 아님) |
| 코드페이지 / CP_UTF8 | 콘솔의 문자 인코딩 설정. UTF-8로 바꿔 유니코드 깨짐 방지 |
| windows-sys | Win32 API를 Rust에서 직접 호출하는 저수준 바인딩 크레이트 |
| winresource | exe에 매니페스트·아이콘·버전 정보를 박아 넣는 빌드 크레이트 |
| WinGet | 윈도우 공식 패키지 관리자. winget install Microsoft.Coreutils |
| PSReadLine | PowerShell의 입력 편집 모듈. 설치 시 인용부호 처리를 유닉스식에 맞춰 통합 |
| panic = "abort" | 패닉 시 되감기(unwind) 없이 즉시 종료 — 바이너리 크기 ~50% 절감 |
| LTO | Link Time Optimization. 링크 단계에서 코드를 다듬어 크기·속도 개선 |