(追記あり: 2025-01-29)
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 } }
2025-01-29 追記
次の記事によると、外部コマンドとのパイプラインが ANSI と解釈されてしまう問題は、[System.Console]::OutputEncoding を変更することで解決するらしい。
これを使って、書き直した結果は以下のとおり:
$url = "https://api.github.com/repos/$owner/$repos/releases" $saveEncode = [System.Console]::OutputEncoding [System.Console]::OutputEncoding=[System.Text.Encoding]::UTF8 curl $url | ConvertFrom-Json | ForEach-Object { foreach ($p in $_.assets){ Write-Host $p.name $p.download_count } } [System.Console]::OutputEncoding=$saveEncode