はじめに
PowerShellでのテキスト処理が、やはり簡単ではないことが多いと感じてきました。PowerShellから外部コマンドやwslに渡すために試したことをメモにまとめました。
結論
- 外部コマンドに渡す場合は
$OutputEncoding
を[console]::OutputEncoding
に揃える$OutputEncoding = [console]::OutputEncoding
-
wsl
に渡す場合は$OutputEncoding
をutf-8
にする$OutputEncoding = [System.Text.Encoding]::UTF8
試したこと
デフォルトでの動作
PS > ls \ | oss | sls c:
ディレクトリ: C:\
PS > ls \ | findstr C:
??????: C:\
PS > ls \ | wsl grep C:
??????: C:\
7bit分しか渡されていないようです。
これは、$PSDefaultParameterValues
を変更しても変わりませんでした。
調べると、下記の記事がヒットしました。
$OutputEncoding
の設定が必要ということがわかりました。
ドキュメントを良く読む
このセクションの最後の部分に以下のように書いてありました。
自動変数は $OutputEncoding 、外部プログラムとの通信に PowerShell が使用するエンコードに影響します。
ということで、$OutputEncoding
を調べるとデフォルトではus-ascii
になっていました。
PS > $OutputEncoding.WebName
us-ascii
$OutputEncoding
について、ドキュメントを見てみましょう。
findstr.exe
に渡す場合は、以下のように設定する必要があると書かれています。
PowerShell でコマンドを機能させるには、 の値を コンソールの $OutputEncoding OutputEncoding プロパティの値に設定します。これは、Windows に対して選択されたロケールに基づいて行います。
$OutputEncoding = [console]::OutputEncoding
これで何が設定されるかを実際に調べてみると、
PS > [console]::OutputEncoding
BodyName : iso-2022-jp
EncodingName : 日本語 (シフト JIS)
HeaderName : iso-2022-jp
WebName : shift_jis
WindowsCodePage : 932
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
IsSingleByte : False
EncoderFallback : System.Text.InternalEncoderBestFitFallback
DecoderFallback : System.Text.InternalDecoderBestFitFallback
IsReadOnly : True
CodePage : 932
こうなっていました。コンソールの出力と、$OutputEncoding
とが一致していないと文字化けするということですね。
$OutputEncoding
に何が設定出来るのかを知るために、以下のリンクを辿ってみました。
既定値: ASCIIEncodingオブジェクト。
各プロパティの継承元を追ってみます。
(継承元 Encoding)
この中に一覧表があり、ここで「.NET Framework サポート」のチェックがあるものが設定可能ということがわかりました。
この表の下に、以下のメソッドを見つけました。
GetEncoding(Int32) GetEncoding(String)
コードページかエンコードの名前を用いて文字エンコードが指定できるということですね。
クラスがSystem.Text.Encoding
ということもわかったので、試してみます。
PS > [System.Text.Encoding]::GetEncoding(65001).webname
utf-8
PS > [System.Text.Encoding]::GetEncoding('utf-8').webname
utf-8
他にも指定する方法がないか、調べてみました。
PS > [System.Text.Encoding] | get-member -static
TypeName: System.Text.Encoding
Name MemberType Definition
---- ---------- ----------
Convert Method static byte[] Convert(System.Text.Encoding srcEncoding, System.Text.Encoding dstEncoding...
Equals Method static bool Equals(System.Object objA, System.Object objB)
GetEncoding Method static System.Text.Encoding GetEncoding(int codepage), static System.Text.Encoding GetEn...
GetEncodings Method static System.Text.EncodingInfo[] GetEncodings()
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
RegisterProvider Method static void RegisterProvider(System.Text.EncodingProvider provider)
ASCII Property static System.Text.Encoding ASCII {get;}
BigEndianUnicode Property static System.Text.Encoding BigEndianUnicode {get;}
Default Property static System.Text.Encoding Default {get;}
Unicode Property static System.Text.Encoding Unicode {get;}
UTF32 Property static System.Text.Encoding UTF32 {get;}
UTF7 Property static System.Text.Encoding UTF7 {get;}
UTF8 Property static System.Text.Encoding UTF8 {get;}
UTF8
、Default
、ASCII
のプロパティが使えそうです。
PS > [System.Text.Encoding]::UTF8.WebName
utf-8
PS > [System.Text.Encoding]::Default.WebName
shift_jis
PS > [System.Text.Encoding]::ASCII.WebName
us-ascii
これで、必要な文字エンコードを直接指定することができるようになりました。
期待通りの動作
PS > $OutputEncoding = [console]::OutputEncoding
PS > ls \ | findstr C:
ディレクトリ: C:\
PS > $OutputEncoding = [System.Text.Encoding]::UTF8
PS > ls \ | wsl grep C:
ディレクトリ: C:\
$OutputEncoding
を固定して使う場合は、$profile
ファイルに書き込んでおくと便利です。
確認環境
- Windows 10 Home 21H2
PS > (gi function:ver).definition
(systeminfo | ?{$_.Contains("OS")})[0,1];($PSVersionTable | oss)[3].trim() -replace ' +',":`t`t";(wsl -l)[3].Split()[0]
PS > ver
OS 名: Microsoft Windows 10 Home
OS バージョン: 10.0.19044 N/A ビルド 19044
PSVersion: 5.1.19041.1320
Ubuntu-18.04
※ (wsl -l)
の文字化けは調査中(上記は文字化け結果を利用したもの)
参考