Brut - Unix 도구를 위한 Brutal Router
요약
이 글은 셸 스크립트의 이식성 문제와 복잡성을 다루며, POSIX 표준 준수의 중요성을 강조합니다. 다양한 운영체제 및 셸 환경(Bash, zsh, dash 등) 간의 차이점과 호환성 문제를 지적하고, 이를 해결하기 위한 여러 시도들(Modernish, OSH 등)을 비교 분석합니다.
핵심 포인트
- POSIX 표준 준수는 스크립트 이식성을 확보하는 핵심입니다.
- 다양한 셸 환경 간의 명령어 및 문법 차이가 심각합니다.
- 단순히 기능을 통합하는 것보다 모듈화된 언어 사용이 더 효율적일 수 있습니다.
- 쉘 스크립팅은 내부적인 모듈화 구조가 부족하다는 근본적인 한계가 있습니다.
POSIX 셸만 쓰면 이식성이 해결된다는 접근은 믿기 어려움. POSIX Is Not A Shell이 이를 잘 짚고 있으며, 관련 토론은 https://news.ycombinator.com/item?id=48711403 에서 볼 수 있음.
예를 들어 sh -c "echo 'a\nb'"는 두 줄을 출력하지만, bash -c "echo 'a\nb'"는 a\nb를 그대로 출력함. Alpine의 /bin/sh인 BusyBox ash와 Debian의 dash는 다른 배포판에서 쓰는 Bash와 상당히 다름. macOS의 /bin/sh도 Bash 3에서 zsh로 바뀌었는지, 별도로 유지되는지 확실하지 않음.
정말 여러 셸에서 동작하도록 만들려면 https://github.com/modernish/modernish 같은 복잡한 우회책에 이르게 됨. Modernish는 안전한 변수·명령 확장과 새로운 반복 구문 등을 제공하지만, 결국 셸을 다른 프로그래밍 언어로 바꾸는 수준임.
Oils의 OSH는 Bash의 큰 부분집합을 현실적인 이식 대상으로 만들어 주며, 세계에서 Bash 호환성이 가장 높은 셸임. 호환성 결과는 https://pages.oils.pub/spec-compat/2025-11-02/… 에서 확인 가능함. YSH는 별도 언어이며, 이런 언어는 Modernish처럼 셸로 구현하기보다 인터프리터 수준의 네이티브 코드로 구현하는 편이 나음.
POSIX는 안정적으로 동작하는 printf 를 제공하므로 도움이 됨.
macOS 10.15 Catalina에서 바뀐 것은 기본 대화형 셸이며, macOS 15 Sequoia에서도 /bin/sh는 여전히 Bash 3.2.57임.
현명한 결정이라고 봄. macOS의 /bin/sh가 Bash 3라고 명시적·암묵적으로 가정하는 스크립트가 무척 많을 텐데, 대화형 셸을 새것으로 바꾸면서도 그 가정은 유지할 수 있기 때문임.
그런데 Bash를 sh라는 이름으로 실행하면 그 프로그램은 어떻게 평가되나요?
목표를 좀 더 명확하게 밝혀 줬으면 함. 셸 스크립트 작성용 프레임워크인가요?
전부 읽었는데도 무엇을 하는 도구인지 이해하지 못했음. 어떤 용도로 유용한지, 무엇을 이루려는지 감조차 잡히지 않아 생성형 AI가 만든 농담인가 싶을 정도였음.
저자를 공격하려는 뜻은 아님. 요즘은 이해되지 않는 것을 보면 통째로 생성된 것은 아닌지 의심하게 됨. Unix 초보자도 아닌데 사용 목적을 모르겠다는 것이 의아하지만, 내가 이해하지 못한 것일 수도 있음.
열악한 개발 환경을 쓰고, 어지럽게 흩어진 파일을 어떻게든 배치하고, 플랫폼마다 다른 POSIX 기본 유틸리티의 특성까지 우회해야 함. 그 대가가 수많은 프로세스 생성과 느린 실행 속도라면 별로 설득되지 않음.
적어도 내게는 이 도구의 최종 목표가 매력적이지 않음. git add와 git commit을 하나의 바이너리로 묶는 이유는 단순히 git-add 대신 git add를 입력하기 위해서가 아니라, Git 파일 구조를 해석하고 다루는 방대한 공통 로직을 공유하기 위해서임.
내가 세어 보니 git에는 하위 명령이 178개 있고 바이너리 크기는 4.7MB임. 대부분이 공통 코드라고 가정하면 각 명령을 별도 바이너리로 만들 때 800MB 이상이 중복되고 빌드 과정도 꽤 번거로워짐.
더 나은 셸 스크립팅의 가능성에 대해 우리가 발휘하는 상상력이 의외로 부족한 것 같다. 다만 이 도구를 만든 동기는 여전히 이해하려고 노력 중이다.
출발점은 셸에 내부적인 모듈화 수단이 발달하지 않았다는 점인 듯하다. Python에는 pip·conda, 라이브러리 배포와 import, 웹 프레임워크 같은 상위 구조가 있지만, 셸에서는 시스템 패키지 관리자와 별도 실행 파일, 커널이나 systemd의 프로세스 실행 기능에 의존한다.
그렇다면 하나의 셸 프레임워크, 즉 이 프로젝트가 말하는 “라우터”로 작업을 통합해서 얼마나 얻을 수 있을지는 의문이다. 차라리 모듈화와 구조화 도구를 갖춘 언어로 통합하면 어떨까 싶다. 정적 링크 바이너리나 Python venv 같은 의존성 격리 수단도 있으며, 시스템마다 일관되게 동작하도록 배포하려면 Janet이나 Zig 실행 파일을 배포해도 된다. 셸 스크립트나 그 디렉터리를 배포한다고 큰 이점이 생기는 것은 아니지만, POSIX 셸로 작성해야 하는 비용은 따른다.
실제로 “POSIX 셸”을 작성한다는 것 자체에도 회의적이다. 제대로 준수했는지 확인할 자료와 도구가 드물고, 현실적으로는 Bash·Dash·BusyBox ash에서 무난하게 동작하는 제한된 부분집합으로 더 표준화된 도구를 실행한 뒤, 사용자 불만이 들어오면 대응할 가능성이 크다.
오히려 셸의 유용함은 뒤섞이고 고르지 않은 성격에 있다고 본다. 업무용으로 직접 작성한 셸 코드가 아마 200개 이상의 스크립트에 5만 줄이 넘으며, 일상 작업용부터 다시 볼 일이 없는 프로젝트용까지 다양하다. 체계적인 지침이 부족한 zsh에서 나만의 작성 스타일을 만드는 데 여러 해가 걸렸고, 잘 만든 코드와 그렇지 않은 코드가 공존한다. 그 불균일함이야말로 셸이 수많은 유용한 작업을 해낸 이유라고 생각한다. 기능 추가가 아니라면 잘 동작하는 스크립트를 추상적인 우아함 때문에 고칠 동기는 없다.
공통 라이브러리와 프레임워크도 여러 번 만들어 봤지만 매번 실패했다. 결과물이 너무 취약하게 조합되므로, 효과적인 셸 스크립팅에는 분해하고 분리할 수 있는 자동화가 더 적합하다는 결론에 가까워진다. Pacman의 PKGBUILD, mkinitcpio, arch-boxes 같은 특정 운영 영역에서는 프레임워크식 구조가 잘 작동하지만, 셸의 조직화 수단 부족을 해결하려면 그 결핍 자체를 다뤄야 한다.
탐구할 여지는 풍부하다. Linux 네임스페이스는 매우 유용하지만 논리적·기계적으로 자연스럽게 조합되지 않으므로, 셸에서 이를 조합하는 수단을 만들면 작업을 단순화하면서 정확성도 크게 높일 수 있다. 파이프 개념의 확장도 진전이 적어 보인다. 저자의 다른 프로젝트인 dyad가 brut보다 흥미롭고 유용할 가능성이 크지만, 이 역시 더 깊이 생각해 볼 여지가 있다.
최근 Zig 작업에서 느낀 것은 유용한 도구일수록 더 깊은 호출 구조에 편입된다는 사실이다. 셸 스크립트가 다른 셸 스크립트, Python의 subprocess.run, 라이브러리, 명령줄 도구, REST API를 거쳐 다시 셸에서 호출될 수 있다. 거의 모든 질문에 코드를 더 작성하는 방식으로 답하는 LLM은 이런 깊이를 크게 늘릴 것이다. brut처럼 셸이 셸을 거듭 호출하는 라우팅 구조는 성능 등의 이유로 효과적인 방향 같지 않다. 그래도 조합의 복잡성을 관리하고, 분해·분리를 고려해 설계하며, 새로운 조합 수단을 만드는 일 자체는 매우 가치 있다.
명령어를 네임스페이스로 묶는 발상은 마음에 들고, Brut이 나오게 된 배경도 이해합니다. 다만 새로운 '언어'와 프로그램 작성법을 배워야 하는 것은 그리 반갑지 않습니다.
POSIX에서도 Plan 9처럼 디렉터리 아래에 별도의 프로그램을 두고 git/clone $REPO나 ip/add $IP/$MASK dev $DEV로 간단히 호출할 수 있으면 좋겠습니다. 상대 경로 명령을 현재 디렉터리뿐만 아니라 $PATH의 각 디렉터리에서 탐색하면 될 것 같습니다. 실행 파일 호출 방식은 명세가 규정하지 않을 것이라 생각해서 POSIX 위반도 아닐 거라고 여겼습니다.
수정: 젠장, POSIX가 이미 규정하고 있었습니다! 슬래시로 시작하지 않는 경로는 프로세스의 현재 작업 디렉터리 또는 특정 인터페이스에 전달된 파일 디스크립터가 가리키는 디렉터리를 기준으로 해석하도록 되어 있습니다. POSIX.1-2024 - General concepts, 4.16 Pathname Resolution을 참조하시면 됩니다.
Brut이 왜 필요한지 설명해 주실 수 있나요? 읽어봐도 이해가 되지 않아 제가 바보처럼 느껴집니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기