macOS pbcopy가 한글을 조용히 버릴 때 — 원인은 비어 있는 $LANG
pbcopy로 한글이 든 파일을 복사했는데 클립보드가 비어 있다. 에러도 없고 종료 코드는 0. 영문은 정상. 원인은 $LANG이 비어 있어 pbcopy가 비ASCII 입력을 통째로 버리는 것이었다.
문제
한글이 든 텍스트 파일을 클립보드에 넣으려 했다.
1
pbcopy < prompt.txt
종료 코드 0. 에러 없음. 그런데 붙여넣으면 아무것도 안 나온다.
1
2
pbpaste | wc -c
# 0
파일은 멀쩡했다.
1
2
wc -c prompt.txt
# 3872 prompt.txt
원인
처음엔 샌드박스나 권한 문제로 의심했다. 클립보드 접근이 막혔나 싶어 샌드박스를 끄고 다시 해봤다. 똑같이 0바이트. 파일 경로를 의심해 cat file | pbcopy로도 해봤다. 역시 0바이트.
그런데 이걸 해보니 갈렸다.
1
2
3
4
5
echo "test123" | pbcopy && pbpaste
# test123 ← 된다
echo "한글테스트" | pbcopy && pbpaste
# ← 빈 줄
영문은 되고 한글은 안 된다. 권한 문제가 아니라 인코딩 문제다.
1
2
echo "locale: [$LANG]"
# locale: []
$LANG이 비어 있었다.
pbcopy는 입력 인코딩을 LANG/LC_CTYPE에서 읽는다. 로케일이 비어 있으면 기본 인코딩(Mac OS Roman 계열)으로 해석하는데, UTF-8 한글 바이트열은 여기서 유효하지 않다. 그래서 입력을 통째로 버린다. 그러고도 종료 코드는 0이다.
터미널을 직접 쓸 때는 .zshrc가 로케일을 잡아주니 이 문제를 안 겪는다. 이 버그는 로그인 셸을 거치지 않는 환경에서 나온다 — CI 러너, launchd/cron으로 뜬 프로세스, 에이전트 도구가 띄운 셸 등.
해결
로케일을 명시한다.
1
LANG=ko_KR.UTF-8 LC_ALL=ko_KR.UTF-8 pbcopy < prompt.txt
en_US.UTF-8이어도 된다. 중요한 건 언어가 아니라 .UTF-8 부분이다.
그리고 복사한 다음엔 반드시 검증한다. 이 버그의 본질은 “조용히 실패한다”는 것이라, 확인하지 않으면 다음 단계까지 빈 클립보드를 들고 간다.
진단 → 수정 → 검증. 마지막 줄의 바이트 수 비교가 핵심이다.
1
2
LANG=ko_KR.UTF-8 pbcopy < prompt.txt
[ "$(pbpaste | wc -c)" = "$(wc -c < prompt.txt)" ] && echo OK || echo MISMATCH
launchd나 CI처럼 셸 설정이 없는 환경이면 아예 환경변수로 박아두는 게 낫다.
1
2
3
4
5
<key>EnvironmentVariables</key>
<dict>
<key>LANG</key>
<string>ko_KR.UTF-8</string>
</dict>
🧭 기획자·사업자라면
- “에러가 없다”와 “성공했다”는 다른 말이다. 이 버그는 종료 코드 0으로 실패한다. 자동화 파이프라인을 점검할 때 “실패 알림이 안 왔으니 잘 돌고 있다”는 가장 위험한 가정이다.
- 한글 환경은 기본값이 아니다. 해외 도구·CI·SaaS는 ASCII를 기본 가정으로 만들어진다. 비ASCII가 조용히 잘리는 사고는 클립보드뿐 아니라 파일명·CSV·PDF 생성에서도 같은 형태로 반복된다.
사용한 기술
pbcopy/pbpaste— macOS 클립보드 CLI. 입력 인코딩을 로케일에서 읽는다.LANG/LC_ALL/LC_CTYPE— 로케일 환경변수.LC_ALL이 나머지를 전부 덮어쓴다.- 로그인 셸 vs 비로그인 프로세스 —
.zshrc/.bash_profile을 읽지 않는 실행 경로에서는 PATH·로케일 같은 기본값이 없다고 가정해야 한다.
정리
pbcopy가 빈 클립보드를 만들면 권한이 아니라 로케일을 먼저 의심한다. 영문만 되는지 테스트하면 1초에 갈린다.LANG=ko_KR.UTF-8을 붙이고, 복사 후 바이트 수를 비교해 검증한다.- 조용히 실패하는 명령은 성공을 확인하는 습관으로만 막을 수 있다. 종료 코드 0은 “했다”지 “됐다”가 아니다.
이 문제를 만난 원래 작업은 여기 → 맥미니를 24시간 봇 러너로 만들기 — launchd + caffeinate 상주 프로세스의 함정 두 개
