about_Character_Encoding
about_Character_Encoding
PowerShell이 문자열 데이터를 입력하고 출력할 때 문자 인코딩을 어떻게 다루는지 설명하는 문서예요. 글자 깨짐 없이 파일을 저장하고 읽으려면 인코딩 개념을 꼭 알아야 하니까, 차근차근 같이 살펴볼게요.
본문
짧은 설명
PowerShell이 문자열 데이터를 입력·출력할 때 사용하는 문자 인코딩 방식을 설명해요.
자세한 설명
Unicode는 전 세계에서 사용하는 문자 인코딩 표준이에요. 시스템은 문자나 문자열을 다룰 때 Unicode만 사용해요. Unicode의 모든 면을 자세히 알고 싶다면 The Unicode Standard 문서를 참고해 주세요.
Windows는 Unicode와 전통적인 문자 집합(character set)을 모두 지원해요. Windows 코드 페이지 같은 전통적인 문자 집합은 8비트 값 또는 8비트 값의 조합으로 특정 언어나 지역 설정에 쓰는 문자를 표현해요.
PowerShell은 기본적으로 Unicode 문자 집합을 써요. 하지만 여러 cmdlet에는 다른 문자 집합의 인코딩을 지정할 수 있는 Encoding 매개 변수가 있어요. 이 매개 변수 덕분에 다른 시스템이나 애플리케이션과 연동할 때 필요한 인코딩을 골라 쓸 수 있죠.
Encoding 매개 변수를 지원하는 cmdlet은 다음과 같아요.
- Microsoft.PowerShell.Management
Add-ContentGet-ContentSet-Content
- Microsoft.PowerShell.Utility
Export-ClixmlExport-CsvExport-PSSessionFormat-HexImport-CsvOut-FileSelect-StringSend-MailMessage
BOM(Byte-Order-Mark) 이야기
BOM(Byte-Order-Mark)은 파일이나 텍스트 스트림의 맨 앞 몇 바이트에 들어가는 Unicode 서명이에요. 이 서명이 데이터에 어떤 Unicode 인코딩이 쓰였는지 알려줘요. 자세한 내용은 Byte order mark 문서를 참고해 주세요.
Windows PowerShell에서는 UTF7을 제외한 모든 Unicode 인코딩이 BOM을 만들어요. PowerShell(6 이상 버전)은 모든 텍스트 출력에 기본적으로 utf8NoBOM을 사용해요.
전반적인 호환성을 생각하면 UTF-8 파일에는 BOM을 쓰지 않는 게 좋아요. Unix 플랫폼이나 Windows에서도 쓰는 Unix 계열 유틸리티는 BOM을 지원하지 않거든요.
마찬가지로 UTF7 인코딩도 피하는 게 좋아요. UTF-7은 표준 Unicode 인코딩이 아니고, 모든 버전의 PowerShell에서 BOM 없이 기록되거든요.
Unix 계열 플랫폼에서 PowerShell 스크립트를 만들거나 Windows에서 Visual Studio Code 같은 크로스 플랫폼 에디터로 만들면 UTF8NoBOM으로 인코딩된 파일이 생겨요. 이런 파일은 PowerShell에서는 잘 돌아가지만, 파일에 non-Ascii 문자가 있으면 Windows PowerShell에서는 문제가 생길 수 있어요.
스크립트에 non-Ascii 문자를 써야 한다면 UTF-8 with BOM으로 저장해 주세요. BOM이 없으면 Windows PowerShell이 스크립트를 옛날 "ANSI" 코드 페이지로 인코딩된 것으로 잘못 해석해 버려요. 반대로 UTF-8 BOM이 들어간 파일은 Unix 계열 플랫폼에서 골치 아플 수 있어요. cat, sed, awk 같은 Unix 도구나 gedit 같은 에디터 중에는 BOM을 제대로 처리하지 못하는 것들이 있거든요.
Windows PowerShell에서의 문자 인코딩
PowerShell 5.1에서 Encoding 매개 변수는 다음 값을 지원해요.
Ascii— Ascii(7-bit) 문자 집합을 사용해요.BigEndianUnicode— big-endian 바이트 순서의 UTF-16을 사용해요.BigEndianUTF32— big-endian 바이트 순서의 UTF-32를 사용해요.Byte— 문자 집합을 바이트 시퀀스로 인코딩해요.Default— 시스템의 활성 코드 페이지(보통 ANSI)에 해당하는 인코딩을 사용해요.Oem— 시스템의 현재 OEM 코드 페이지에 해당하는 인코딩을 사용해요.String—Unicode와 같아요.Unicode— little-endian 바이트 순서의 UTF-16을 사용해요.Unknown—Unicode와 같아요.UTF32— little-endian 바이트 순서의 UTF-32를 사용해요.UTF7— UTF-7을 사용해요.UTF8— UTF-8(BOM 포함)을 사용해요.
일반적으로 Windows PowerShell은 기본적으로 Unicode UTF-16LE 인코딩을 사용해요. 다만 Windows PowerShell에서 cmdlet들이 쓰는 기본 인코딩이 항상 일관된 건 아니에요.
[!NOTE]
UTF7을 제외한 어떤 Unicode 인코딩을 쓰든 항상 BOM이 만들어져요.
파일에 출력을 쓰는 cmdlet:
Out-File과 리다이렉션 연산자>와>>는 UTF-16LE를 만들어요. 이 점은Set-Content,Add-Content와 확실히 달라요.New-ModuleManifest와Export-Clixml도 UTF-16LE 파일을 만들어요.- 대상 파일이 비어 있거나 없으면
Set-Content와Add-Content는Default인코딩을 사용해요.Default는 활성 시스템 로캘의 ANSI 레거시 코드 페이지가 지정하는 인코딩이에요. Export-Csv는Ascii파일을 만들지만, Append 매개 변수를 쓰면 다른 인코딩을 사용해요(아래 참고).Export-PSSession은 기본적으로 BOM이 있는 UTF-8 파일을 만들어요.New-Item -Type File -Value는 BOM이 없는 UTF-8 파일을 만들어요.Send-MailMessage는 기본적으로Ascii인코딩을 사용해요.Start-Transcript는 BOM이 있는Utf8파일을 만들어요. Append 매개 변수를 쓰면 인코딩이 달라질 수 있어요(아래 참고).
기존 파일에 추가(append)하는 명령:
Out-File -Append와>>리다이렉션 연산자는 기존 대상 파일 내용의 인코딩을 맞추려고 시도하지 않아요. 대신Encoding매개 변수를 쓰지 않으면 기본 인코딩을 사용해요. 내용을 추가할 때는 원래 파일 인코딩을 꼭 써야 해요.- 명시적인
Encoding매개 변수가 없으면Add-Content는 기존 인코딩을 감지해서 새 내용에 자동으로 적용해요. 기존 내용에 BOM이 없으면DefaultANSI 인코딩을 사용해요.Add-Content의 동작은 PowerShell(6 이상)에서도 같은데, 기본 인코딩만Utf8이라는 점이 달라요. Export-Csv -Append는 대상 파일에 BOM이 있으면 기존 인코딩을 맞춰요. BOM이 없으면Utf8인코딩을 사용해요.Start-Transcript -Append는 BOM이 있는 파일의 기존 인코딩을 맞춰요. BOM이 없으면 기본적으로Ascii인코딩을 사용해요. 이 인코딩은 트랜스크립트 안의 데이터에 멀티바이트 문자가 있으면 데이터 손실이나 문자 깨짐을 일으킬 수 있어요.
BOM이 없을 때 문자열 데이터를 읽는 cmdlet:
Get-Content와Import-PowerShellDataFile는DefaultANSI 인코딩을 사용해요. ANSI는 PowerShell 엔진이 파일에서 소스 코드를 읽을 때도 사용하는 인코딩이에요.Import-Csv,Import-Clixml,Select-String은 BOM이 없으면Utf8이라고 가정해요.
PowerShell에서의 문자 인코딩
PowerShell(7.1 이상)에서 Encoding 매개 변수는 다음 값을 지원해요.
ascii— ASCII(7-bit) 문자 집합의 인코딩을 사용해요.ansi— 현재 문화권의 ANSI 코드 페이지 인코딩을 사용해요. 이 옵션은 PowerShell 7.4에서 추가됐어요.bigendianunicode— big-endian 바이트 순서를 사용해 UTF-16 형식으로 인코딩해요.bigendianutf32— big-endian 바이트 순서를 사용해 UTF-32 형식으로 인코딩해요.oem— MS-DOS와 콘솔 프로그램용 기본 인코딩을 사용해요.unicode— little-endian 바이트 순서를 사용해 UTF-16 형식으로 인코딩해요.utf7— UTF-7 형식으로 인코딩해요.utf8— UTF-8 형식(BOM 없음)으로 인코딩해요.utf8BOM— BOM(Byte Order Mark)이 있는 UTF-8 형식으로 인코딩해요.utf8NoBOM— BOM(Byte Order Mark)이 없는 UTF-8 형식으로 인코딩해요.utf32— little-endian 바이트 순서를 사용해 UTF-32 형식으로 인코딩해요.
PowerShell은 모든 출력에 기본적으로 utf8NoBOM을 사용해요.
PowerShell 6.2부터는 Encoding 매개 변수에 등록된 코드 페이지의 숫자 ID(예: -Encoding 1251)나 등록된 코드 페이지의 문자열 이름(예: -Encoding "windows-1251")도 전달할 수 있어요. 자세한 내용은 .NET 문서의 Encoding.CodePage를 참고해 주세요.
PowerShell 7.4부터는 Encoding 매개 변수에 ANSI 값을 쓰면 현재 문화권의 ANSI 코드 페이지 숫자 ID를 직접 적지 않아도 자동으로 넘겨줘요.
기본 인코딩 바꾸기
기본 인코딩 동작을 바꾸는 데 쓸 수 있는 PowerShell 기본 변수가 두 개 있어요.
$PSDefaultParameterValues$OutputEncoding
자세한 내용은 about_Preference_Variables를 참고해 주세요.
PowerShell 5.1부터 리다이렉션 연산자(>와 >>)는 Out-File cmdlet을 호출해요. 그래서 아래 예시처럼 $PSDefaultParameterValues 기본 설정 변수로 이들의 기본 인코딩을 정해줄 수 있어요.
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
Encoding 매개 변수가 있는 모든 cmdlet의 기본 인코딩을 바꾸려면 다음 명령문을 사용해요.
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
[!IMPORTANT] 이 명령을 PowerShell 프로필에 넣으면 이 기본 설정이 세션 전역 설정이 되어, 인코딩을 명시적으로 지정하지 않는 모든 명령과 스크립트에 영향을 줘요.
마찬가지로 같은 동작을 원하는 스크립트나 모듈에도 이런 명령을 넣는 게 좋아요. 이렇게 하면 다른 사용자가 다른 컴퓨터에서, 다른 버전의 PowerShell로 실행해도 cmdlet이 똑같이 동작해요.
자동 변수 $OutputEncoding은 PowerShell이 외부 프로그램과 통신할 때 쓰는 인코딩에 영향을 줘요. 출력 리다이렉션 연산자나 PowerShell cmdlet이 파일을 저장할 때 쓰는 인코딩에는 아무 영향이 없어요.