PowerShellのスクリプトを書く時の諸注意
先日Windows向けの作業のちょっとした自動化のためにPowerShellのスクリプトを書いたので、少し躓いたポイントを書きます。
大人しくLinux版のPowerShellもインストールします
WSL2のEmacsから快適に編集するためにシンタックスなどのエラーレポートなどがやはり欲しいですね。
Powershell - LSP Mode - LSP support for Emacs
を使えば普通にLSPが利用可能ですが、インストールするためにpwsh
が実行ファイルとしてPATH
に認識されている必要があります。
しかしWSL2上で作業しているのですから、
Windows上のpwsh.exe
を使えば多重にインストールする必要がなくなるのではと考えました。
そこでlsp-pwsh-exe
にpwsh.exe
を追加してみたのですが、
Windows上のPowerShellをWSL2上で動かすと、
tempディレクトリを作る時にパスのルートの位置を誤認するようで動きませんでした。
インストール手順などをもう少し工夫すればいけそうな気もしますが、他にもハマりポイントがありそうなので、大人しくLinux版をインストールすることにしました。
今はWSL2にはUbuntuを使っているので、 Microsoftのリポジトリを以下からインストールすることにしました。
bash, zshで言うset -e
は$ErrorActionPreference = "Stop"
外部コマンドなどが失敗した時には当然スクリプト全体を止めたくて、続行して欲しい時は殆ど無いと思いますが、その場合のzshで言うset -e
には$ErrorActionPreference = "Stop"
が該当します。
実行ログを見たい場合はRead-Host
かPause
Linux向けスクリプトではどうせスクリプトはターミナルから実行してれると思うのでいちいち配慮しないのですが、素人がエクスプローラなどから動かすかもしれない、 Windows向けスクリプトでは最後にEnterを押さないと終了しないようにした方が良いでしょう。
エクスプローラを経由した.bat
ファイルからの実行も考慮するなら尚更です。
Pause
コマンドか、
Read-Host "Enterを押したら終了します"
のようなものを最後に書いておくとウィンドウが自動で閉じません。
ただ一つ問題がありまして、前述の$ErrorActionPreference = "Stop"
を使うと機能しません。
ちゃんとtry-catchで制御すると問題ないのかもしれませんが、そのような複雑な処理をPowerShellで書くべきかと言うと微妙なラインだと思います。
Windows向けPowerShellでは文字コードはUTF-8(BOMあり)で改行コードはCRLFにします
改行コードはCRLFにします
気が付きにくい罠なのですが、改行コードはLFではなくCRLFにしましょう。
例えば以下のようなPowerShellスクリプトを書いた場合、
# `stable`ブランチに切り替え、存在しない場合は`origin/stable`から作成します。
if ($null -eq (git branch --list stable)) {
git switch -c stable origin/stable
}
else {
git switch stable
}
PowerShell 5で実行すると、
PS C:\Users\ncaq\Downloads> .\play.ps1
発生場所 C:\Users\ncaq\Downloads\play.ps1:3 文字:1
+ }
+ ~
式またはステートメントのトークン '}' を使用できません。
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
のようにエラーが出ます。
何故かと言うと、コメントが正常に改行で終了してない扱いになるみたいですね。なので2行目などがなかったことになります。
コメントを含まないスクリプトの場合動いたり動かなかったりします。少し謎です。
パースエラーのエラー表示ではご覧のように改行されて出力されるので気がつくのが遅れました。
ちなみにPowerShell 7だと正常に実行出来ます。
文字コードはUTF-8にする場合BOMをつけます
Windows向けだろうと流石にUTF-8でソースコードは記述したいものです。
PowerShell 7では自動で認識するようです。
しかし、
Windows 10/11で標準で実行されるPowerShell 5では、
UTF-8にBOMが無いと日本語Windowsでは認識されるのか、
Write-Host
などで文字化けが発生します。
BOMをつけるとちゃんとUTF-8で書かれていると認識して出力してくれます。
chcp
とかでガチャガチャするより大多数のシステムの類推で動くように任せるほうがまだ安心できますね。
Emacsならばset-buffer-file-coding-system
でutf-8-with-signature-dos
を選択しておけば良いでしょう。
頻繁に忘れるのでEmacsのinit.el
で設定してしまっても良いかもしれませんね。
まとまった記事がありました
後から調べたら詳細は以下の記事にまとまっていました。
改行コードについての記載はありませんが概ね下記のような理解で問題ないかと思います。
OSは
Windows
を利用してWindows PowerShell
を利用する場合UTF8+BOM + CRLF
OSは
Windows
とLinux
のクロスプラットフォームでWindowsPowerShell
とPowerShell
が混在する場合UTF8+BOM + CRLF
OSは
Linux
だけでPowerShell
のみ利用UTF-8+BOMless + LF
とりあえず今の所まだLinux向けにPowerShellを使う気が私には無いので、 UTF-8にBOMをつけておけば良いでしょう。
Linuxなどでも動かしてshebangに対応することを考えると、 PowerShell 7を前提にすること以外に解決策が無いのがなんだか虚しいですが。
.editorconfig
の設定
次から間違えることを防止するために.editorconfig
を設定しておきましょう。
プロジェクト全体ではcharset = utf-8
, end_of_line = lf
を設定しているので尚更です。
[*.bat]
end_of_line = crlf
charset = latin1
[*.ps1]
end_of_line = crlf
charset = utf-8-bom
署名のないPowerShellスクリプトがWindowsの設定次第で実行しづらいことに対処するために、同名のバッチファイルを作ってPowerShell -NoProfile -ExecutionPolicy Unrestricted
で実行するテクニックを使っているので、バッチファイル向けの設定もしています。
コマンドプロンプトのバッチファイルのUTF-8へのスマートな対応には全く期待してないのでユニコード文字は不許可にしています。
どうせPowerShellを起動する単純なスクリプトしか書かないのでcharset = latin1
としています。これはASCIIと入力したかったのですが、
EditorConfig Properties · editorconfig/editorconfig Wiki
を見てもus-ascii
とかが存在しなかったので妥協しました。サポートしている実装はそれなりにあるのかもしれませんが。
とりあえず日本語圏では日本語を入力させないというだけでそれなりの働きはしてくれると思います。
何故PowerShell 7はWindowsのデフォルトの設定では無いのでしょうか
しかし何故MicrosoftはWindows 11のリリースタイミングで、 PowerShellの標準バージョンをレガシーな5からマルチプラットフォームの.NETベースで開発されたPowerShell 7にしなかったのでしょう? PowerShell 5を起動すると、
新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows
と表示されるあたり、別に本流になったPowerShell 7に興味がないというわけでもないでしょう。
PowerShell 6がリリースされたのは2016年で、本流として.NET CoreからCoreのサフィックスが外れたPowerShell 7.1のリリースから数えても2020年なので、それなりの期間が過ぎています。
もう安定しているかどうか分からないから本流にするか悩む段階を過ぎていると思います。
デフォルトをPowerShell 7系列にして、互換性のために一応PowerShell 5を残しておく程度で良いと思います。
はてなブックマークのコメントによる補足: PowerShell 7の互換性はまだまだらしい
PowerShellのスクリプトを書く時の諸注意
PowerShell7以降(pwsh)は、Windows PowerShell(いわゆる5.1)のモジュールが全然入ってない別物。故に7系はPowerShellの中心的なニーズであるWindowsの管理系作業では使い物にならない。7系がデフォルトになんて今の段階じゃあり得ん。
2023/07/29 20:08
なるほど、
Windows 管理モジュールでは、Windows のさまざまな機能やサービスの管理とサポートが提供されます。 これらのモジュールのほとんどは、PowerShell 7 でネイティブに動作するように更新されているか、PowerShell 7 との互換性がテストされています。
とか書かれていたので殆ど互換性があるものかと考えてしまいましたが、まだまだモジュールの互換性が取れてないんですね。
具体的にどういうものが互換性が取れてなくてクリティカルに不便なのかは、 PowerShell 7 module compatibility | Microsoft Learn を見てもそんなにWindowsに詳しくない私にはすぐにはピンと来ませんでしたが。
コア部分での互換性は取れているはずなので、互換レイヤーでのモジュール対応も含めて、コーナーケースをひたすらに潰していったらいつか置き換わる日がくるかもしれませんね。 Microsoftもいつまでも2つの.NET、 2つのPowerShellをメンテナンスするのは嫌だと思うので。