PowerShellでは、echo、cat、lsといったよく知られたコマンドは最初から用意されています。ただし、これらは別名(alias)であって、PowerShell本来の名前は Write-Output、Get-Content、Get-ChildItem です。コマンドラインから対話的に使うには短い名前が便利なので別名を使うことにします。
さて、コマンドラインの例:
PS C:\tmp> echo hello
hello
PS C:\tmp> echo hello > hello.txt
PS C:\tmp> cat hello.txt
hello
PS C:\tmp>
特に珍しいことは何もないですよね。しかし、
PS C:\tmp> ls hello.txt
ディレクトリ: C:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/25 13:04 16 hello.txt
PS C:\tmp>
あれ? ファイルのサイズが変ですよ。bashに移って、
$ od -x hello.txt
0000000 feff 0068 0065 006c 006c 006f 000d 000a
0000020$
左端は行番号みたいなもの(バイトオフセット)なので、データは feff, 0068, 0065, 006c, 006c, 006f, 000d, 000a、最初がバイトオーダーマーク、最後は改行の CR, LF ですが、16ビット単位です。そうです、UTF-16なんですよね。
echo(Write-Output)に文字エンコーディングを指定するようなパラメータ(オプション)はありません。リダイレクト記号「>」を使う代わりに次のようにすると文字エンコーディングを指定できます。
PS C:\tmp> "hello" | out-file -encoding:utf8 hello.txt
PS C:\tmp>
それでファイルサイズは?
PS C:\tmp> ls hello.txt
ディレクトリ: C:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/25 13:28 10 hello.txt
PS C:\tmp>
んんー? 3バイト余計です。
$ od -x hello.txt
0000000 bbef 68bf 6c65 6f6c 0a0d
0000012$ od -o hello.txt
ああー、またBOM(バイトオーダーマーク)だ。BOMなしのUTF-8の指定はないのか?
PS C:\tmp> "hello" | out-file -encoding:utf8n hello.txt
Out-File : パラメーター 'Encoding の引数を確認できません。引数 "utf8n" は、ValidateSet 属
性で指定されたセット "unknown、string、unicode、bigendianunicode、utf8、utf7、utf32、asci
i、default、oem" に属していません。このセットの引数を指定して、コマンドを再度実行してくだ
さい。
発生場所 行:1 文字:30
+ "hello" | out-file -encoding:utf8n hello.txt
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Out-File]、ParameterBindingValidationExce
ption
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Comm
ands.OutFileCommandPS C:\tmp>
ダメみたい。調べたけど、BOMなしUTF-8の出力はできないようです。自前でどうにかするしかないようなので、次の関数を書きました。
function Get-Fullpath ([string] $path) { $current = (pwd).path $path = [IO.Path]::Combine($current, $path) [IO.Path]::GetFullPath($path) } function Out-FileInUtf8N ([string] $path){ begin { $text = "" } process { $text += ($_ + "`r`n") } end { $Utf8N = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllText((Get-Fullpath $path), $text, $Utf8N) } } Set-Alias out8 Out-FileInUtf8N
なんとかなったかな。
PS C:\tmp> "hello" | out8 hello.txt
PS C:\tmp> ls hello.txt
ディレクトリ: C:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/25 14:14 7 hello.txt
PS C:\tmp> "こんにちは" | out8 hello-ja.txt
PS C:\tmp> ls hello-ja.txt
ディレクトリ: C:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/25 14:15 17 hello-ja.txt
PS C:\tmp>
ちょっと面倒くさいなー。
あーそれと、PowerShellのecho(Write-Output)は、cmd.exeのechoとは挙動が違いますから要注意。
PS C:\tmp> echo hello world
hello
world
PS C:\tmp> echo hello> hello.txt
hello>
hello.txt
PS C:\tmp>