증상: 한글이 든 .ps1 이 예약 실행에서 결과 코드 1로 죽고, 파워셸 출력을 받은 프로그램에서는 한글 경로 비교가 늘 거짓이 됩니다.
원인: 파워셸 5.1 은 BOM 없는 스크립트와 출력을 모두 CP949 로 다룹니다.
해결: 스크립트는 UTF-8 BOM 으로 저장하고, 출력을 받을 때는 명령 맨 앞에서 OutputEncoding 을 UTF-8 로 바꿉니다.
윈도우 기본 파워셸 5.1 은 한글을 두 방향에서 깨뜨립니다. 들어오는 스크립트 파일을 읽을 때 한 번, 결과를 내보낼 때 한 번입니다. 둘 다 오류 없이 조용히 틀리는 경우가 많아서 원인을 찾기까지 오래 걸렸습니다.
오류가 나거나, 아무 오류 없이 틀리거나
첫 번째로 겪은 일은 매일 02:00 에 도는 야간 작업이 아침마다 죽어 있던 것입니다. 작업 스케줄러의 LastTaskResult 는 1 이었고, 스크립트가 만들어야 할 로그 파일조차 생기지 않았습니다. 같은 파일을 손으로 powershell -File 로 돌리니 그제야 파싱 오류가 보였습니다.
Unexpected token '숇큺?뚯씠??=' in expression or statement.
긴 배치 스크립트 하나는 한글 변수 이름이 통째로 깨지면서 이런 문법 오류를 28건 냈습니다. 반대로 한글 정규식으로 로그를 기다리는 감시 스크립트는 오류가 하나도 없었는데, 매칭에 영원히 실패하며 무한 대기에 빠졌습니다.
두 번째는 출력 쪽입니다. 백그라운드로 도는 노드 프로그램이 파워셸로 프로세스 명령줄을 받아 "내 설치 경로가 들어 있나"로 자기 프로세스인지 가렸습니다. 그런데 비교가 늘 거짓이라 자기 자신을 남의 것으로 보고 10분마다 죽였다 켰고, 하루 144회 재시작이 쌓였습니다. 이때도 오류는 없었습니다.
세 번째는 git commit -m 에 히어스트링을 넘겼을 때 메시지 안의 큰따옴표에서 글이 잘리고, 뒷부분이 파일 경로로 읽혀 커밋이 실패한 일입니다.
5.1 의 기본 인코딩이 CP949 다
| 증상 | 원인 |
|---|---|
| 예약 실행 결과 코드 1, 로그 없음 | BOM 없는 UTF-8 스크립트를 ANSI(한국어 윈도우 = CP949)로 읽어 파싱 실패 |
| 한글 정규식이 영원히 안 맞음 | 같은 이유로 패턴 글자가 깨져서 비교 대상이 달라짐 |
| 한글 경로 비교가 늘 거짓 | 파워셸은 CP949 로 내보내는데 노드는 UTF-8 로 읽음 |
| 커밋 메시지가 큰따옴표에서 잘림 | 5.1 이 네이티브 exe 인자 속 큰따옴표를 이스케이프하지 않음(7.3 에서 고쳐졌다고 알려짐) |
함정이 하나 더 있습니다. 구문 검사를 Get-Content -Raw -Encoding UTF8 로 읽어서 하면 UTF-8 로 읽었으니 통과합니다. 하지만 -File 실행에는 인코딩 옵션이 없어 CP949 로 읽고 죽습니다. 검사는 초록인데 예약 실행만 실패하는 이유가 이것입니다.
들어오는 쪽과 나가는 쪽을 따로 고친다
1. 한글이 든 .ps1 은 UTF-8 BOM 으로 다시 저장
BOM 이 있으면 5.1 도 UTF-8 로 알아봅니다. 편집기에 따라 BOM 없이 저장되므로 아래 한 줄로 다시 씁니다.
$p = "C:\scripts\nightly.ps1"
$c = Get-Content $p -Raw -Encoding UTF8
[System.IO.File]::WriteAllText($p, $c, (New-Object System.Text.UTF8Encoding $true))
가능하면 스크립트 안에서는 한글 대신 ASCII 패턴으로 매칭하고, 데이터 파일은 [System.IO.File]::ReadAllText(경로, 인코딩) 로 인코딩을 밝혀 읽습니다.
2. 파워셸 출력을 다른 프로그램이 읽을 때
명령 맨 앞에 출력 인코딩을 UTF-8 로 바꾸는 문장을 붙입니다.
powershell -NoProfile -Command "[Console]::OutputEncoding=[Text.Encoding]::UTF8; Get-CimInstance Win32_Process | Select-Object ProcessId, CommandLine"
경로로 자기 것인지 가릴 때는 한글이 없는 고유한 꼬리(예: \worker\run_worker.py)로도 비교합니다. 글자판이 또 어긋나도 판정이 무너지지 않습니다.
3. 커밋 메시지는 파일로
git commit -F .\commit_msg.txt
4. 긴 배치는 파이썬으로
결국 가장 안전했던 길은 긴 배치를 파이썬으로 짜고, 파워셸은 그것을 띄우는 얇은 시작 명령으로만 쓰는 것이었습니다.
확인 방법
- 예약 작업을
Start-ScheduledTask로 한 번 돌리고Get-ScheduledTaskInfo의 LastTaskResult 가 0 인지, 로그가 생겼는지 봅니다. - 구문 검사를 믿지 말고 반드시
powershell -File로 실제 실행해 봅니다. - 출력을 받는 프로그램은 받은 문자열을 그대로 로그에 찍어 한글이 멀쩡한지 눈으로 봅니다.
같이 알아 두면 좋은 5.1 의 특징도 있습니다. robocopy 종료 코드 0에서 7은 모두 성공이라 0 이 아니라고 실패로 처리하면 오탐이 납니다. 삼항 연산자와 null 병합 연산자는 5.1 에 없어서 if/else 로 써야 합니다.
확인한 환경
| 항목 | 내용 |
|---|---|
| 운영체제 | 한국어 윈도우(기본 코드 페이지 CP949) |
| 파워셸 | Windows PowerShell 5.1 |
| 실행 방식 | 작업 스케줄러, powershell -File, 노드에서 호출 |
관련 글
이 글의 내용은 2026년 10월 5일에 마지막으로 확인했습니다. 틀린 곳을 발견하시면 연락처로 알려 주세요.