Skip to content

Instantly share code, notes, and snippets.

@stknohg
Last active July 6, 2019 10:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stknohg/9793cddd3311e1e0941d3b25b6f8cd35 to your computer and use it in GitHub Desktop.
Save stknohg/9793cddd3311e1e0941d3b25b6f8cd35 to your computer and use it in GitHub Desktop.
エンコーディング設定を変更したスクリプトブロックを実行するファンクション
<#
.SYNOPSYS
ファイル出力に関わるエンコーディング設定を変更したスクリプトブロックを実行します。
.DESCRIPTION
指定したスクリプトブロック内でのみファイル出力をするコマンドレット(Out-Fileなど)のエンコーディングの挙動を変更します。
現在変更可能な点は以下になります。
1. 通常BOM付きのUTF8をBOM無しのUTF8に変更する
2. DEFAULTエンコーディングを指定したコードページのエンコーディングに変更する
.PARAMETER UseBOMlessUTF8
UTF8パラメーターで指定されるエンコーディングをBOM無しUTF8に変更します。
.PARAMETER DefaultCodePage
Defaultパラメーターのエンコーディングを指定したコードページのエンコーディングに変更します。
.EXAMPLE
PS C:\> Invoke-CustomEncodingBlock { "BOMなしUTF8" | Out-File .\BOMless.txt -Encoding utf8 } -UseBOMlessUTF8
この例ではOut-Fileコマンドレットの出力をBOM無しUTF8にします。
.EXAMPLE
PS C:\> Invoke-CustomEncodingBlock { "デフォルトEUC(51932)" | Out-File .\Default51932.txt -Encoding default } -DefaultCodePage 51932
この例ではOut-Fileコマンドレットの出力をEUC(コードページ51932)にします。
#>
function Invoke-CustomEncodingBlock {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[ScriptBlock]$Block,
[Parameter(Mandatory = $false)]
[Switch]$UseBOMlessUTF8 = $false,
[Parameter(Mandatory = $false)]
[int]$DefaultCodePage = [System.Text.Encoding]::Default.WindowsCodePage
)
# PowerShell 6.0ではエンコーディングの取り扱いが変わったためこの関数を使う必要が無い
if ($PSVersionTable.PSVersion.Major -ge 6) {
Write-Error "PowrShell 6.0以降でこの関数は使えません。"
return
}
# 変更判定
$isChangeUTF8 = $UseBOMlessUTF8
# * .NET Coreだと $null -eq [System.Text.Encoding]::Default なため [int]の値で比較する
$isChangeDefault = $DefaultCodePage -ne [int]([System.Text.Encoding]::Default.WindowsCodePage)
# 変更なしの場合は単純にスクリプトブロックを実行して終了
if (-not $isChangeUTF8 -and -not $isChangeDefault ) {
$Block.Invoke()
return
}
try {
# 既定の動作を変更
$type = [System.Text.Encoding]
if ($isChangeUTF8) {
$UTF8wBOM = [System.Text.Encoding]::UTF8
$UTF8woBOM = New-Object "System.Text.UTF8Encoding" -ArgumentList @($false)
$mUTF8 = $type.GetMember("utf8Encoding", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static)
if ($mUTF8.Length -eq 0) {
throw ("Type member {0} not found." -f "utf8Encoding")
}
$mUTF8[0].SetValue($mUTF8[0], $UTF8woBOM)
}
if ($isChangeDefault) {
$currentDefaultEncoding = [System.Text.Encoding]::Default
$newDefaultEncoding = [Text.Encoding]::GetEncoding($DefaultCodePage)
# PS 5.0以降は System.Management.Automation.ClrFacade クラスにエンコーディングのキャッシュがあるのでここを変える
if ($PSVersionTable.PSVersion.Major -lt 5) {
$mDefault = $type.GetMember("defaultEncoding", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static)
if ($mDefault.Length -eq 0) {
throw ("Type member {0} not found." -f "defaultEncoding")
}
$mDefault[0].SetValue($mDefault[0], $newDefaultEncoding)
} else {
$asm = $MyInvocation.GetType().Assembly
$typeFacade = $asm.GetType("System.Management.Automation.ClrFacade")
# PS 5.0 - 5.1 は _defaultEncoding
# PS 6.0(.NET Core)だと s_defaultEncoding っぽい...
foreach ($name in @("_defaultEncoding", "s_defaultEncoding")) {
$mDefault = $typeFacade.GetMember($name, [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static)
if ($mDefault.Length -ne 0) {
break
}
}
if ($mDefault.Length -eq 0) {
throw ("Type member {0} not found." -f "defaultEncoding")
}
$mDefault[0].SetValue($mDefault[0], $newDefaultEncoding)
}
}
# Scriptblock実行
$Block.Invoke()
} finally {
if ($isChangeUTF8) {
if ($null -ne $mUTF8) {
$mUTF8[0].SetValue($mUTF8[0], $UTF8wBOM)
}
}
if ($isChangeDefault) {
if ($null -ne $mDefault) {
$mDefault[0].SetValue($mDefault[0], $currentDefaultEncoding)
}
}
}
}
#
# ちょっとだけテスト
#
Import-Module Pester
Describe "UseBOMlessUTF8パラメーターのテスト" {
It "BOMなしUTF8 - 最初の3ByteがBOMでないこと" {
$filePath = ".\BOMless.txt"
Invoke-CustomEncodingBlock { "BOMなしUTF8" | Out-File $filePath -Encoding utf8 } -UseBOMlessUTF8
$bytes = (Format-Hex -InputObject (Get-Item $filePath)).Bytes
$bytes[0..2] | Should Not Be @(0xEF, 0xBB, 0xBF)
}
It "BOMなしUTF8 - ファイルの内容が正しく出力されていること" {
$filePath = ".\BOMless.txt"
Invoke-CustomEncodingBlock { "BOMなしUTF8" | Out-File $filePath -Encoding utf8 } -UseBOMlessUTF8
$bytes = (Format-Hex -InputObject (Get-Item $filePath)).Bytes
$bytes | Should Be @(0x42, 0x4F, 0x4D, 0xE3, 0x81, 0xAA, 0xE3, 0x81, 0x97, 0x55, 0x54, 0x46, 0x38, 0x0D, 0x0A)
}
# 必ず Invoke-CustomEncodingBlock を実行した後にテストする
It "BOM付きUTF8 - 最初の3ByteがBOMであること" {
$filePath = ".\BOM.txt"
"BOMありUTF8" | Out-File .\BOM.txt -Encoding utf8
$bytes = (Format-Hex -InputObject (Get-Item $filePath)).Bytes
$bytes[0..2] | Should Be @(0xEF, 0xBB, 0xBF)
}
It "BOM付きUTF8 - ファイルの内容が正しく出力されていること" {
$filePath = ".\BOM.txt"
"BOMありUTF8" | Out-File .\BOM.txt -Encoding utf8
$bytes = (Format-Hex -InputObject (Get-Item $filePath)).Bytes
$bytes | Should Be @(0xEF, 0xBB, 0xBF, 0x42, 0x4F, 0x4D, 0xE3, 0x81, 0x82, 0xE3, 0x82, 0x8A, 0x55, 0x54, 0x46, 0x38, 0x0D, 0x0A)
}
}
Describe "DefaultCodePageパラメーターのテスト" {
It "Default変更 - ファイルの内容が正しくEUC(CP51932)になっていること" {
$filePath = ".\Default51932.txt"
Invoke-CustomEncodingBlock { "デフォルト51932" | Out-File $filePath -Encoding default } -DefaultCodePage 51932
$bytes = (Format-Hex -InputObject (Get-Item $filePath)).Bytes
$bytes | Should Be @(0xA5, 0xC7, 0xA5, 0xD5, 0xA5, 0xA9, 0xA5, 0xEB, 0xA5, 0xC8, 0x35, 0x31, 0x39, 0x33, 0x32, 0x0D, 0x0A)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment