1,400 줄의 bash 와 23 개의 스크립트를 삭제하고, Bun Shell TypeScript 로 교체했습니다. Bun Shell ($ ... ) 은 타입화된 인수, 자동 이스케이프, .text(), .json(), .lines() 를 기본 제공하며 jq, awk, 그리고 대부분의 GNU coreutils 의존성을 제거합니다. 스크립트는 이제 약 30ms 만에 부팅됩니다. Bash 는 여전히 인터랙티브 REPL 과 매우 긴 파이프라인에서 우위를 점하지만, 나머지는 모두 Bun 으로 이동했습니다.
저는 과거에 모든 프로젝트에 scripts/ 폴더를 유지했습니다. 그것은 항상 썩었습니다. 인용구 버그, set -e 누락, 그리고 기계 간에 일치하지 않는 jq 버전들입니다. 지난 분기에는 23 개를 Bun Shell 로 마이그레이션하고 오후 한 번에 1,400 줄의 bash 를 삭제했습니다.
왜 Bash 스크립트는 항상 혼자 스튜디오에서 썩는가?
저는 많은 글루 코드를 작성합니다. 빌드 푸시, 변경 로그 게시, CDN 에 이미지 미러링, 두 개의 env 파일 차이 비교, 로그 꼬임 및 정규식 검색을 수행합니다. 오랫동안 그것은 .sh 파일로 살았습니다. 작성한 날에는 작동했지만, 6 주 후에 재현할 수 없는 이유로 깨졌습니다.
Bash 는 4 가지 문제를 계속 맞았습니다. 인용구가 그 중 하나입니다. "$file" versus $file versus '$file' 이 중요하며, 차이가 공간이 있는 경로가 나타날 때까지 보이지 않습니다. set -euo pipefail 은 필수적이지만 스크립트 번호 12 에는 아무도 기억하지 못합니다. 오류는 조용히 삼키며, 3 주 후에 밤새 작업이 빈 파일을 작성하고 있음을 발견합니다. 그리고 타입은 없으므로 --dry-run versus -n versus --dry 는 항상 동전 던지기입니다.
포터블리티 스토리는 더 나쁩니다. 내 노트북에는 Home brew 를 통해 GNU coreutils 가 있습니다. CI 런너는 BSD coreutils 입니다. sed -i 는 다른 인수를 사용합니다. 날짜 형식은 다릅니다. jq 는 한 박스는 1.6, 다른 박스는 1.7 이었고, 하나의 스크립트가 1.7 전용 플래그에 의존하고 있었지만 제가 알아채지 못했습니다. 모든 크로스 플랫폼 수정은 또 다른 if [[ "$OSTYPE" == "darwin"* ]] 분지를 추가합니다.
혼자 스튜디오의 살인자는 23:00 에 60 줄의 shell 스크립트를 디버깅할 수 없다는 것입니다. 저는 스크립트가 실행되거나 정확히 지정한 줄을 가리키는 스택 트레이스로 쾅쾅하게 실패해야 합니다. Bash 는 두 가지도 주지 않습니다.
저는 Python 을 오랫동안 시도했습니다. venv plus boto3 plus requests 의 시작 시간은 차가운 800ms 로, 이는 좋지만 hot loop 안에 호출할 때까지입니다. 저는 zx (Node + JS 템플릿 문자열) 를 시도했습니다. 작동했지만 모든 스크립트 폴더에 Node, npm, 그리고 node_modules 디렉토리를 끌었습니다. 그리고 Bun 1.2 가 모든 새로운 RAXXO 프로젝트에서 Node 를 대체하고, 저는 거의 우연히 Bun Shell 을 시도했습니다.
Bun Shell 정신 모델
Bun 은 태그된 템플릿으로 내장 shell 을 제공합니다. import $ from bun 과 명령어를 인라인으로 작성합니다:
import { $ } from "bun";
const branch = await $ git rev-parse --abbrev-ref HEAD.text();
console.log(Deploying ${branch.trim()});
세 가지가 bash 를 시작하는 것에서 다릅니다. 인수는 인터폴레이션 시 자동으로 이스케이프되므로, $ mv ${userInput} /tmp` 는 ; rm -rf / 로 속임수를 당할 수 없습니다. 반환 값은 .text(), .json(), .lines(), .blob(), .arrayBuffer() 와 같은 체인 가능한 프롬리스이며, .quiet(), .nothrow(), .cwd(path), .env({...}) 와 같은 수정자도 있습니다. 그리고 macOS, Linux, Windows 에서 동일하게 작동하며 Bun 은 /bin/sh 를 감싸는 래퍼가 아니라 자체 최소 shell 을 제공합니다.
저가 가장 많이 사용하는 패턴 몇 가지:
javascript // 명령어에서 JSON 직접 읽기
const tags = await $ git tag --list .lines();
// 출력을 억제하고, 0 이 아닌 경우에도 던지지 않음
const result = await $ grep ERROR app.log .quiet().nothrow();
if (result.exitCode === 0) await alert(result.stdout.toString());
// 명령어 간 파이프링, 여전히
inside template await $ cat data.csv | sort -u > clean.csv ; Cold startup on my M2 is around 30ms for a Bun script that does one shell call, versus around 250ms for the equivalent zx script and around 800ms for Python with boto3 . For a script that runs inside a watch loop or a git hook, that gap matters. The thing I did not expect: I no longer need jq , awk , sed , cut , or tr for most jobs. Bun ships Bun.file() , JSON.parse , regex, and .replaceAll() . A bash one-liner like cat events.json | jq '.[].user' | sort -u | wc -l becomes: javascript const events = await Bun.file(
macOS 에서 느린 프로세스를 프로파일링할 때 dtrace | grep | awk | sort | uniq -c | sort -rn | head 와 같은 파이프라인을 사용한다. 이를 TypeScript 로 재작성하는 것은 아무런 도움이 되지 않는다. 파이프 6 개로 연결된 바이너니는 bash 의 영역이다. 나는 이 것을 .sh 파일로 유지하고, 필요할 때 Bun 에서 호출한다. POSIX 만 지원되는 환경들. 몇몇 CI 러너와 가끔 Docker 기본 이미지는 Bun 을 설치하지 않았고, 내가 추가할 수 없다. 그들을 위해 Bash 를 사용하여 Bun 을 설치한 후 전달하는 작은 부트스트랩 스크립트를 유지한다. 부트스트랩은 12 줄이며 6 개월 동안 변경되지 않았다. 긴 파이프라인에 대한 우회책은 간단하다: Bun Shell 은 bash 자체를 포함한 임의의 바이너니를 호출할 수 있다. 내가 정말로 200 자의 파이프라인을 원한다면, await $ bash -c "${pipeline}"`` 을 작성하고 Bun 이 스폰, 출력 포착, 에러 코드를 처리한다. 나는 프로젝트당 하나 또는 두 개의 것을 유지하며 잘 주석 처리하고 예외로 취급한다. 또한 나의 pre-commit hook 은 Bash 로 유지한다. 2 년 동안 6 줄이며 재작성하는 것은 허영심이다. 마이그레이션의 좋은 부수 효과: my scripts/ 폴더는 이제 프로젝트의 나머지 TypeScript 와 함께 배송된다. 동일한 tsconfig.json 이 이를 린트한다. 동일한 Biome 규칙이 이를 포맷팅한다. 스크립트와 테스트 파일에서 사용되는 함수를 이름 변경할 때, LSP 가 둘 다 감지한다. Bash 는 나에게 그걸 주지 않았다. Bottom Line 나는 다시 돌아가지 않을 것이다. Bun Shell 은 Python 과 zx 가 결코 제대로 하지 못했던 곳에 맞았다: 타입화된 인수, 자동 이스케이프, node_modules 없음, ~30ms cold start, 그리고 macOS, Linux, CI 러너 모두에서 조건부 브랜치 없이 동일한 스크립트 작동한다. 마이그레이션은 영웅적인 재작성이 아니었다. 나는 커피 한 잔당 하나의 스크립트를 작성하고, Bash 버전을 git history 에 유지하며, 나머지는 단순한 pre-commit hook 이거나 파이프라인으로 더 나은 경우를 멈췄다. 만약 당신이 솔로 스튜디오를 운영한다면, 당신의 deploy 또는 notifier 스크립트부터 시작할 것이다. 그 것들은 금요일 밤 11 시에 깨지고 가장 많은 비용이 든 디버깅을 한다. Bun 이 나의 스택에 어떻게 들어가는지에 대한 나머지는 Bun 의 테스트 러너가 새로운 프로젝트에서 Vitest 을 대체하며, 테스트 측면을 커버하고 Studio 는 이 스택이 실제로 배송하는 프로젝트를 보여준다.