Windows PowerShellで動くスクリプトがPowerShellでも動くとは限らない、なぜなのか?:山市良のうぃんどうず日記(279)
Microsoftは2024年2月初め、「回復パーティション」のサイズを拡張するサンプルスクリプトを公開しました。もし、このサンプルスクリプトを実行したい場合は、クロスプラットフォーム対応の「PowerShell 7.x」ではなく、Windowsのコンポーネントである「Windows PowerShell 5.1」の使用をお勧めします。なぜなら、サンプルスクリプト内で使用されている「Split」メソッドの挙動に重大な違いがあるからです。
Windowsコンポーネントの「Windows PowerShell」とオープンソースの「PowerShell」
Windowsのコンポーネントである「Windows PowerShell」は「5.1」が現行バージョンであり、その後のロードマップは示されていません。
その代わり、Microsoftはオープンソースプロジェクトである「PowerShell」(旧称:PowerShell Core)を提供し、機能強化やパフォーマンス改善を実施してきました。現在の最新バージョンは「7.4.1」です。「PowerShell Core 6」のリリース以降、Windows PowerShellを起動すると、「新しいクロスプラットフォームのPowerShell」を試すように案内されるようになりました(画面1)。ちなみに、「powershell.exe -nologo」を実行すれば、この案内なしでWindows PowerShellを起動できます。
「.NET Framework」上に構築されたWindows PowerShellに対し、PowerShellは「.NET」(旧称、.NET Core)上に構築され、Windows、macOS、Linuxのクロスプラットフォーム対応です。Windows上ではWindows PowerShellとの共存が可能で、既存のWindows PowerShellモジュールとの互換性を提供します。Windows PowerShellに含まれるモジュールやコマンドの一部は、さまざまな理由からPowerShellから削除されていますが、削除されていないモジュールやコマンドを使用する限り、Windows PowerShellで動作するスクリプトはそのままPowerShellでも動くはずです。
- Windows PowerShell 5.1とPowerShell 7.xの相違点(Microsoft Learn)
Windows PowerShellでは問題ないのにPowerShellではエラー! なぜなのか?
冒頭で触れたMicrosoft提供のサンプルスクリプトは、2024年1月の「Windows回復環境(Windows RE、WinRE)」更新プログラムのインストールエラー回避策の一つとして示されたものです。
- 2024年1月のWindows RE更新プログラムのインストールに失敗する場合があります(Microsoft Learn)
- Windows REに更新プログラムパッケージを追加する > Windows REのパーティションを拡張する(Microsoft Learn)
以下の画面2は、このサンプルスクリプトのコピーを管理者として開いたWindows PowerShell(powershell.exe)とPowerShell(pwsh.exe)でそれぞれ実行した結果です。
Windows PowerShellでは問題なくスクリプトが進みますが(実際の拡張にはユーザーの指示が必要です)、PowerShellでは「Get-Partiton」コマンドレットがエラーとなり、スクリプトが途中でストップしてしまいます。
PowerShellのエラー原因は「Split」メソッドの挙動の違い
何がエラーとなっているのか、調査したところ、サンプルスクリプト内にある「$WinRELocationItems = $WinRELocation.Split('\\')」の部分が返す結果の違いが原因となっていました(同じコードはサンプルスクリプト内に2カ所あります。
…… # Get WinRE partition $WinRELocationItems = $WinRELocation.Split('\\') foreach ($item in $WinRELocationItems) { if ($item -like "harddisk*") { $OSDiskIndex = ExtractNumbers($item) } if ($item -like "partition*") { $WinREPartitionIndex = ExtractNumbers($item) } } ……
「$WinRELocation」には、このコードに入る前までに「reagentc /info」のWinREの場所(WinREが有効な場合)を含むパス(例:\\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE)が入っています。
このパスを「Split()」メソッドで「\」を区切り文字として分割し、「harddiskX」と「partitonY」を取得して、最終的にWinREを含む回復パーティション番号(XとY)を取得しようとしています。
問題は、「Split()」メソッドの返す結果が、Windows PowerShellとPowerShellで異なる点にあります。
以下の画面3を見てください。Windows PowerShellでは「Split("\\")」は「\」で文字列を分割できてますが、PowerShellでは分割されていません。「Split("\")」と書き換えると、どちらも分割されます。
画面3 Windows PowerShellでは「Split()」メソッドで「\」記号をエスケープしてもしなくても同じ結果が返ってくるが、PowerShellではエスケープすると期待通りの結果が返ってこない
文字列分割には「-Split」オペレーター(演算子)を使用することも可能です。以下の画面4を見てください。
「-Split」オペレーターでは「\」区切り文字は「\」記号(円、バックスラッシュ)でエスケープすべき文字であることが分かります。「Split()」メソッドは、「-Split」オペレーターのように「\」記号をエスケープする必要がないようにも見えます。言い方を変えると、後出のPowerShellの「Split()」メソッドは仕様に厳格で、Windows PowerShellの「Split()」メソッドはある程度の曖昧さを許容しているように見えます。
これがWindows PowerShellとPowerShellの仕様の違いなのか、どちらか一方がバグなのかどうか分かりませんが、このサンプルコードを書いた人は「-Split」オペレーターと「Split()」メソッドが同じものと勘違いしているのかもしれません。少なくとも、PowerShellで実行するユーザーがいることを想定し、テストされているとは思えませんでした。
実は筆者自身、「-Split」と「Split()」は書き方の違いで、別物とは想像もしていませんでした。そのため、区切り文字の指定方法にも違いはないと思っていました。
以下のドキュメントが別になっていることからも分かるように、「Split()」メソッドは.NET Frameworkや.NETのメソッドの一つであり、「-Split」オペレーターはWindows PowerShellやPowerShellの演算子の一つです。
- .NET(.NET Framewrork) / String.Splitメソッド(Microsoft Learn)
- PowerShell / about_Split(オペレーター)(Microsoft Learn)
そして、これらの使用方法には異なる部分が多くあります。例えば、「-Split」オペレーターでは正規表現を使用できますが(例:-split ",+"はカンマ区切りで空行を除く)、「Split()」メソッドではできません。
これと似たようなケースに、ドット(.)のエスケープがあります。ドット(.)は改行文字を除く任意の文字と一致するように解釈されるため、改行を除く全ての文字列に対して空白行を返します。
そのため、ドット(.)で分割したい場合は、「『「\』記号を使用してドット(.)区切り記号をエスケープするように」と上記の「about_Split」オペレーターのドキュメントに説明されています。このドット(.)の扱いも、PowerShellの「Split()」メソッドでは「\」記号と同様にエスケープする必要はありませんでした。
また、Windows PowerShellではエスケープする/しないに関係なく、同じ結果が返ってきました(画面5)。このことからも、PowerShellの「Spilit()」メソッドの方が本来の実装であると思えます。
もし、あなたが何か管理用のスクリプトを書いたのなら、特にシステムに変更を加える操作をしようとしているのなら、想定される環境で問題がないかどうかをテストしてください。例えば、Windows用のPowerShellスクリプトなら、少なくとも、最新のWindows PowerShell 5.1とPowerShell 7.4.1でテストしましょう。
筆者紹介
山市 良(やまいち りょう)
岩手県花巻市在住。Microsoft MVP 2008 to 2024(Cloud and Datacenter Management)。SIer、IT出版社、中堅企業のシステム管理者を経て、フリーのテクニカルライターに。Microsoft製品、テクノロジーを中心に、IT雑誌、Webサイトへの記事の寄稿、ドキュメント作成、事例取材などを手掛ける。個人ブログは『山市良のえぬなんとかわーるど』。近著は『Windows版Docker&Windowsコンテナーテクノロジ入門』(日経BP社)、『ITプロフェッショナル向けWindowsトラブル解決 コマンド&テクニック集』(日経BP社)。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- Windows 11登場! 11で変わること、思ったほど変わらないこと
新しいWindows OS「Windows 11」の正式出荷が2021年10月5日に開始された。Windows 10からの無償アップグレードが可能であるため、どのような新機能が実装されたのか気になる人も多いのではないだろうか。そこで、本稿ではWindows 11の新機能、削除された機能などを簡単にまとめてみた。 - Windows 11一般提供開始、企業での導入/展開時に注意すべきポイントは?
MicrosoftはWindowsデスクトップOSの最新バージョンである「Windows 11」を正式にリリースし、Windows 11対応ハードウェアを搭載したWindows 10デバイスに対して、無料アップグレードの段階的なロールアウトを開始しました。 - 買って、試して分かったWindows 365(契約・セットアップ編)
Microsoftからクラウド上でWindows 10が動く「クラウドPC」の利用可能なサブスクリプションサービス「Windows 365」の提供が開始された。早速、サブスクリプションを契約し、クラウドPCの設定を行ってみた。契約からセットアップまでで見えてきた便利な点、不便な点などをまとめてみた。 - いよいよ完全終了へ。Internet Explorer(IE)サポート終了スケジュール
長らくWindows OSに標準装備されてきたInternet Explorer(IE)。その「寿命」は各種サポートの終了時期に左右される。Windows OSごとにIEのサポート終了時期を分かりやすく図示しつつ、見えてきた「終わり」について解説する。