Windowsにもcurlコマンドがインストールされて久しいのですが、以下の問題があります。
- curl.exeもあるし、curlコマンドもある
- curlコマンドはInvoke-WebRequestのエイリアスなので、一般的なcurlとは似ても似つかない引数になる
- Invoke-WebRequestは出力もオブジェクトなのでbodyだけ取り出したい時とかひと手間かかる。
- Linuxのcurlコマンドに近いのは、curl.exe
じゃぁcurl.exeを使おうじゃないか
- Linux上でcurlを扱うときには、
curl yahoo.co.jp > out.html
みたいに標準出力をリダイレクトすることが多い - ところが、Windowsの場合標準出力がどうしても「人間様が見るの」という前提があり
- コマンドプロンプトはSJISに変換される
- パワーシェルはUTF16に変換される
- つまり、Windowsのシェルは「人間が見ることを前提」としているため、別の処理にそのまま生の出力をパスするーすることを想定してない。(一応パイプはある)
- 標準出力時に文字コードが強制的に変わることで、一番多いUTF-8のファイルをcurlで扱うことができない
解決策:標準出力を使わない
curl.exe -L yahoo.co.jp -o out.html
-o
オプションを使えば標準出力ではなくファイルに直接書かれる。これならWindows標準出力まともに使えない問題の回避ができる。そしてWindows上のシェル上でパイプやリダイレクトを使うということは諦める。めちゃくちゃ頑張ったら出来るけど、人間がそこまでめちゃくちゃ頑張らなくちゃいけないのならそれはシェルとはいえない。
WindowsのコマンドプロンプトやパワーシェルはいつまでたってもUTF8を直接扱えない。画面の表示をUTF8にすることはできても、コマンドが標準出力に何かを出力するとSJISになる。そして大きく情報が欠落する。
腹を決めてWSLを使う方が圧倒的に「コンピュータがやることはコンピュータ同士に任せる」ことができる。CLIはどうせ人間には対して優しくない。人間はコンピュータ同士の対話を客席から見ることができれば十分だ。
PowerShellでWebレスポンスをエンコード指定で扱う例
そうは言ってもPowerShellがそんなに馬鹿な作りではないはず。当然やろうと思えばできる。
$Response = Invoke-WebRequest -Uri "https://aka.ms/pscore6-docs"
$Stream = [System.IO.StreamWriter]::new('.\docspage.html', $false, $Response.Encoding)
try {
$Stream.Write($Response.Content)
}
finally {
$Stream.Dispose()
}
エンコードを指定しながらSystemをnewして、そこにWriteしている。エンコードはWebサーバーのヘッダで指定しているエンコードなので外れることも多い。このサンプルはいまいちだから参考にしないほうがいい。デコードされたレスポンスをエンコードして出力しているだけなので、生データをコピーしてるわけではない。
>
の代わりとするとめちゃくちゃ行数が増える。スクリプトかもしれないが、シェルの定義からは外れてる。僕たちはただ、インタネット上のファイルを落としてきたいだけなんだ。
まとめるとWindows上で標準入出力を扱いたかったら素直にWSLを使いましょうということです