WSLにShift_JISを渡す方法：PowerShellの意外な使い方

## はじめに
新たに作るテキストファイルは、BOMなしのUTF-8にしていますが、時々Shift_JIS(以下SJIS)のファイルを使うことがあります。WSLを導入した直後から、何も追加インストールせずにSJISファイルをWSL側に渡す方法を見つけたので、紹介します。

## 結論
### ポイント
* コマンドウィンドウ(cmd)を使う
* パイプラインに`|powershell -c $input`を挟む
* SJIS→BOMなしUTF-8変換をWindows 10標準コマンドで実現

### 例

```
C:>type sjis.txt | powershell -c $input | wsl grep S
SJISのテキストです。
```

## 試したこと

### 入力ファイル

```
C:>type sjis.txt
SJISのテキストです。
```

### WSLでcatする

```
C:>type sjis.txt | wsl cat
SJIS̃eLXgłB
```

当然文字化けします。

### WSLでgrepする

```
C:>type sjis.txt | wsl grep S
バイナリファイル (標準入力) に一致しました
```

門前払いで、箸にも棒にもかかりません。

### WSLでcatして、リダイレクトする

```
C:>type sjis.txt | wsl cat >sjis_wsl_cat.txt

C:>type sjis_wsl_cat.txt
SJISのテキストです。
C:>wsl cat sjis_wsl_cat.txt
SJIS̃eLXgłB
```

`|`も`wsl cat`も何も加工していないと考えられます。

### WSLで、catしてリダイレクトする

```
C:>type sjis.txt | wsl cat ^>sjis_wsl_cat_hat.txt

C:>type sjis_wsl_cat_hat.txt
SJISのテキストです。
C:>wsl cat sjis_wsl_cat_hat.txt
SJIS̃eLXgłB
```

どちらでリダイレクトしても結果は同じでした。

### &#x1f44d;PowerShellを挟む

```
C:>type sjis.txt | powershell -c $input | wsl cat
SJISのテキストです。

C:>type sjis.txt | powershell -c $input | wsl grep S
SJISのテキストです。

C:>type sjis.txt | powershell -c $input | wsl grep テ
SJISのテキストです。

```

WSLで普通に扱えるようになりました。
PowerSHellのバッチ処理結果がUTF-8でcmdに返され、その結果を加工せずにWSLに渡していると考えられます。

### &#x1f44d;PowerShellを挟んで、WSLでcatして、リダイレクトする

```
C:>type sjis.txt | powershell -c $input | wsl cat >sjis_ps_wsl_cat.txt

C:>type sjis_ps_wsl_cat.txt
SJIS縺ｮ繝・く繧ｹ繝医〒縺吶・

C:>wsl cat sjis_ps_wsl_cat.txt
SJISのテキストです。

C:>wsl hd sjis_ps_wsl_cat.txt
00000000  53 4a 49 53 e3 81 ae e3  83 86 e3 82 ad e3 82 b9  |SJIS............|
00000010  e3 83 88 e3 81 a7 e3 81  99 e3 80 82 0d 0a        |..............|
0000001e

```

`sjis_ps_wsl_cat.txt`の中身は紛らわしいですが、BOMなしUTF-8のファイルになっています。

### 応用：SJISからUTF-8への変換バッチファイル

```sjis2utf8.bat
@echo Shift_JIS to UTF-8 Converter
@setlocal
@if not defined DSTSUFFIX set DSTSUFFIX=utf8
@if "%~1" == "" goto Usage
@for %%f in (%*) do @call :conv_wsl %%f
@echo all done
@exit /b

:conv_wsl
@set DSTFILE=%~dpn1_%DSTSUFFIX%%~x1
@echo Input: %~f1
@if not exist %1 echo %1 not found!&exit /b
@echo Output:%DSTFILE%
@type %1 | powershell -c $input | wsl cat > %DSTFILE%
@echo done
@exit /b

:Usage
@echo.
@echo Usage: %~n0 'Shift-JIS_file_name'.txt
@echo.
@echo Output: 'Shift-JIS_file_name'_utf8.txt
@exit /b
```
まとめて変換したくなった場合には、こんなバッチファイルを書けば、ワイルドカードなどで簡単に処理できます。

### WSLが使えない場合
32bitのWindows 10を使っていたり、WSLを有効化していない環境で、更にPowerSHellのスクリプトも書きたくない場合は、上記のバッチファイルで14行目を以下のワンライナーに差し替えると、WSLを使わずにBOMなしUTF-8に変換できます。

```bat
@type %1 | powershell -c "[Text.Encoding]::UTF8.GetBytes($input) | sc -Path %DSTFILE% -enc byte"
```

* 参照：[PowerShellでBOM無しUTF8を簡単に扱う、デフォルト設定を簡単に変える方法](https://blog.shibata.tech/entry/2016/10/02/154329)

### 追記：WSLが使えない場合の別解
バッチファイルの冒頭に`chcp 65001`を追加すると、以下のワンライナーで実現できます。必要に応じて最後に`chcp 932`を追加してください。

```bat
@powershell -c cat %1 > %DSTFILE%
```

これとの対称性を考えると、`wsl`を使う場合も、以下に差し替えた方が良いですね。こちらはコードページ`932`のままで使えます。

```bat
@powershell -c cat %1 | wsl cat > %DSTFILE%
```


### 確認環境

* Windows10 Home 21H2
* PowerShell 5.1
* WSL1 (Ubuntu 18.04、20.04)

```
OS 名:                  Microsoft Windows 10 Home
OS バージョン:          10.0.19044 N/A ビルド 19044
PSVersion:              5.1.19041.1320

  NAME            STATE           VERSION
* Ubuntu-18.04    Running         1
```

WSLもPowerShellも初期状態で確認しています。特別な設定は必要ありませんでした。

## 参考

https://qiita.com/e4rfx/items/7cb0be97be552d0068ff

https://qiita.com/e4rfx/items/6e67359b2d5d0d27ccab

https://qiita.com/e4rfx/items/77a0e274b51c6454590f