標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

PowerShell で外部コマンドのからんだパイプラインと文字化け

PowerShell の練習で、GitHub の releases のダウンロード数を表示するコードを書いてみた。

$url = "https://api.github.com/repos/$owner/$repos/releases"

curl $url | ConvertFrom-Json | ForEach-Object {
    foreach ($p in $_.assets){
        Write-Host $p.name $p.download_count
    }
}

ところが次のようなエラーになってしまう。

$ ~\Download-Count.ps1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  107k    0  107k    0     0   204k      0 --:--:-- --:--:-- --:--:--  205k
ConvertFrom-Json: C:\Users\hymkor\Download-Count.ps1:26
Line |
  26 |  curl $url | ConvertFrom-Json | ForEach-Object {
     |              ~~~~~~~~~~~~~~~~
     | Conversion from JSON failed with error: After parsing a value an unexpected
     | character was encountered: u. Path '[5].body', line 1059, position 5.

curl で取得したデータがおかしいのかと思って、一度、ファイルに落として Get-Content tmp.json | ConvertFrom-JSON をしてみたところ、こちらはエラーにならない。

試行錯誤してみたところ、次のように書けば期待どおり動作した。

$url = "https://api.github.com/repos/$owner/$repos/releases"

curl $url | nkf32 -W8 -s | ConvertFrom-Json | ForEach-Object {
    foreach ($p in $_.assets){
        Write-Host $p.name $p.download_count
    }
}

どうやら、「外部コマンド | 内部コマンド」というパイプラインを作ると、外部コマンドの出力は現在のコードページの文字コード、つまり CP932 などと解釈されてしまうためのようだ。

ところが curl はデータをあくまでバイナリデータとして、そのまま標準出力に吐いてくるので、GitHub 由来のデータは UTF8 なのだ。つまり、文字コードが誤認される結果となっていた。

PowerShell も 7.4 で「外部コマンド | 外部コマンド」がうまく動作するようになったが、まだまだ外部コマンドがからむパイプラインは鬼門のようだ。

これ最終的には curl.exe など使わず、PowerShell の機能でデータをダウンロードするよう直すべきなんだろうなぁ。

→ こんな感じで落ちついた

$url = "https://api.github.com/repos/$owner/$repos/releases"

Invoke-WebRequest -URI $url | ConvertFrom-Json | ForEach-Object {
    foreach ($p in $_.assets){
        Write-Host $p.name $p.download_count
    }
}