PowerShellのOut-Fileはパイプラインで繋ぐだけでファイルに出力してくれます。ファイルを開いたり閉じたりの手順が隠蔽されて、とてもPowerShell的で良いのですが、出来上がるテキストファイルがBOM有のUnicodeだったりします。
Unicodeなのは変換で何とかなりそうですがBOM有なのはちょっと面倒です。フラットなテキストファイルを出力する関数を作ってみます。
スクリプト
using namespace System.IO
# BOM無しUTF8でテキストファイルを出力する
function Out-FileWithoutBOM
{
param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
[string[]]$Lines,
[Parameter(Mandatory=$True, ValueFromPipeline=$False)]
[string]$FilePath
)
begin
{
$fs = New-Object StreamWriter($FilePath, $false)
}
process
{
foreach($line in $Lines)
{
$fs.WriteLine($line)
}
}
end
{
$fs.Close()
}
}
$array = @("abc", "123")
# パイプライン
$path = "H:ps1text_file_encode_testoutfile_test1.txt"
$array | Out-FileWithoutBOM -FilePath $path
# 通常呼び出し
$path = "H:ps1text_file_encode_testoutfile_test2.txt"
Out-FileWithoutBOM $array -FilePath $path
説明
BOM無しのUTF8のテキストファイルが出力されるように作りました。
このソースでは省略していますが、System.IO.StreamWriterのコンストラクタの3番目引数にエンコーディングを指定するとBOM付きのテキストファイルになります。
パイプラインに対応するように作ってみたつもりですが、パイプラインに引き渡す変数$Linesがstringの配列になっています。
process内でforeachで要素を取り出してWriteLineで一行ごと書き込みしています。
配列でループさせている理由は通常の関数として呼び出した場合の対応です。
多分パイプラインの場合は引数の文字列($_)が要素が一つの配列に変換されて、一回だけforeachしていると思われます。
beginとendはパイプラインでも通常呼び出しでも最初と最後の1回のみ実行されることを期待していますが、例外を考慮すべきでしょう。
追記20231025:
UTF8への変換処理が見当たらないのですが
UTF8への変換処理が見当たらないのですが
StreamWriter()
のデフォルトのEncodingがUTF8となっていますので、特別変換処理をコーディングしなくともUTF8へ変換しているようです。ワンライナー
Write-Output "日本語" | ForEach-Object -Begin { $fs = [System.IO.StreamWriter]::new( "h:workspaceDummy7.txt") } -Process { $fs.WriteLine($_) } -End { $fs.Close() }
スクリプトを書くのではなくコマンドラインから直接実行出来る形にしてみました。
文字コード変換してバイナリとして書き出す方法
Write-Output "日本語" | ForEach-Object { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Encoding Byte -Path .Dummy2.txt
こちらの方法はコマンドラインから実行出来ます。文字列をUTF8へ変換しByte配列としてファイルに書き出しています。
そうすることでBOM無しUTF8のテキストファイルを作成することが出来ます。
感想
Out-FileのデフォルトがBOM無しUTF8だったらいいな。それが無理でも、せめてオプションで選べるようにしてほしい。
追記:20221006
こちらの情報はPowerShell5(WindowsPowerShell)の内容です。PowerShell7ではBOM無しのUTF8になります。
コメント