PowerShellの罠

PowerShell コンピュータ
PowerShell

PowerShell は、他のシェルスクリプト言語とは異なる独特の挙動を示すことがあり、一般的なシェル言語の感覚で実行すると、思わぬ落とし穴にはまることがあります。ここでは、PowerShell の罠となりうる要素をいくつかご紹介します。

int型へのキャストで小数点以下が切り捨てにならない。

多言語多くはint型へキャストでは切り捨てですが、PowerShell7はそうではありません。
例.

[int](6.6)
# 7 ⇐ 結果

[int](6.5)
# 6 ⇐ 結果

回避策

[Math]::Truncate(6.6)(6.6)
# 6 ⇐ 結果

こういうところで個性を出してくるのがMicrosoft

WindowsPowerShell(PowerShell5)のテキストファイルがBOM有

PowerShell7では解消されてUTF8のBOM無しになっている。
ただ、Windows11で標準インストールされているパワーシェルはWindowsPowerShell(PowerShell5)なので要注意
PowerShellのOut-Fileで出力されるファイルがBOM有Unicodeで驚く
PowerShellのOut-Fileはパイプラインで繋ぐだけでファイルに出力してくれます。ファイルを開いたり閉じたりの手順が隠蔽されて、とてもPowerShell的で良いのですが、出来上がるテキストファイルがBOM有のUnicodeだった...

PowerShellのパイプはオブジェクトが渡せて、うまく使えばよいツールだと思うのですが、最後に出力するテキストファイルがBOM有と一般的ではないスタイルになっているあたりがMicrosoftらしいと思う。

代替案
PowerShell7をインストールし使う

コマンドの戻り値の要素が複数の場合と単数の場合でコードを分ける必要あり

例1.戻り値が変数になるケース

$result1 = Get-Process -Name notepad
($result1 -is [array])
# False ⇐ arrayでは無い = 変数

例2.戻り値がarrayになるケース

$result1 = Get-Process
($result1 -is [array])
# True ⇐ array

同じGet-Processなのに結果が異なります。
動的型の言語だからと言ってこれはどうかと思います。変数ではなく要素が1つのarrayの方が一般的ではないでしょうか?

解決策
変数とarrayどちらも受け入れるコードを書きましょう。そうすると融通のきく素晴らしいスクリプトになります。(コードを書く側は大変ですか…)

配列は固定長

空の配列で初期化して、いざ使おうとするとエラーに成ります。

$array = @()

PowerShellの配列はC言語と同じで固定長ですので、要素数0で初期化すると要素を代入することが出来ません。
解決策
ArrayListなどの.NET Frameworkのコレクションクラスを使用するか、連想配列のキーを数値連番のインデックスにして疑似的に配列にする等が考えられます。

PowerShell におけるチルダ (~) の挙動について

PowerShell 内でパスにチルダ (~) を使うと、ユーザーのホームディレクトリへ展開されることが期待されますが、
外部コマンドのコマンドライン引数では展開されないことが多いです。
ドット(.)も含め、これらの特殊文字をどう解釈するかはアプリケーション側の実装次第であり、期待どおりに動作しない場合があります。
そのため、面倒でもフルパスを記述しておいたほうが、より確実な結果が得られるでしょう。
(残念ながら、Linux のように一貫した動作は期待できません。)

[ ] を含むファイル名のパスを -Path で指定することはできない

PowerShell のコマンドレットでファイルパスなどを引数に指定する場合、引数名には -Path-LiteralPath の2種類があります。
-Path はワイルドカードを展開し、-LiteralPath は展開しません。

一般的にワイルドカードといえば *? が知られていますが、PowerShell では [ ] もワイルドカードの一種(文字クラス)として解釈されます。
そのため、ファイル名に [ ] が含まれていると、-Path を使った指定では正しくファイルを見つけられないという、ややこしい現象に遭遇します。

このような場合は、-LiteralPath を使えば文字通りのファイル名として扱われ、正常に動作します。とりあえず -LiteralPath を使っておけば安全です。

感想

事前に知っておけば回避できますし、PowerShellの個性だと思って受け入れましょう。

コメント