PowerShellで簡単にWindowsリソース系を確認するコマンドの一覧とUnixShellとの違い

## はじめに
PowerShellは現在のWindows NT系に標準搭載されているコマンドラインシェルです。とはいえ、UnixShellとも呼ばれるbashやzshのように利用されているという話はWebアプリ開発・運用の界隈だと意外にもあまり聞きません。会社のPC環境はほとんどがWindowsなことがまだ多いような気はするので、単純にPowerShellを知る機会が少ないのが要因として考えられます。ちなみに私自身もセキュリティ関連でWindowsの内容が多く、それでPowerShellのリソース系を確認するコマンドの存在を知ったほどでした。

そこで、まずCLIが使えるのならばすぐに知りたい(主に自分が)、リソースを確認したりするコマンドの一覧をまとめました。
普段は深いGUIの構造を辿って確認するWindowsのリソースをPowerShellで比較的容易に確認できるようになります。

そして多少深く知りたい人(こちらも主に自分)のために、PowerShellの特徴について書いています。特徴としてはUnixShellのbashやzshと比較して変数、より厳密にはオブジェクトを前提に動かすという部分にあります。これが一体どういった挙動に繋がるのかについて比較をしながら、時に大まかにWindows APIの話もしながら確認をしていきます。



## 動作を確認した環境
普段使いのiMacにParallelsを入れてWindows11をVMとして動作させた環境で実行して確認しました。おそらくWindows 10でも同等の内容で確認ができるとみられます。



## Windowsリソース系を確認するコマンドの一覧

Windowsリソース系を確認するコマンドを備忘録として簡易的な一覧としてまとめています。

### プロセスの確認

タスクマネージャーの一覧をコマンドラインで出力します。Unixでいうところの ps -aに近い挙動です。

```
Get-Process
```

特定のプロセスを見たい場合は以下のようにコマンドラインオプションをいれるとよいです。

```
Get-Process -name PowerShell
```

### イベントログの確認方法

イベントログの場合はsystemの部分をapplicationなどで引数指定しないと利用することができません。かつ、-Newestのフレーズを使わないと過去全てのログが吐かれてしまうので数を制限して確認していくのがおすすめです。

```
get-eventlog system -Newest 5
```

### サービスの一覧情報の確認

Unixでいうところのデーモンプロセスに相当する一覧を出します。Windows Serverを運用している人ならかなりお世話になるかもしれません。

```
Get-Service
```

### ネットワークアダプターの確認

IPアドレスを確認したい場合は以下のコマンドで実行できます。

```
Get-NetIPAddress
```

アダプター自体の情報で、たとえばMACアドレスを確認したい場合は以下のコマンドを使用します。

```
Get-NetAdapter
```

### レジストリ内の環境変数を確認する方法
PowerShellにおいては環境変数もレジストリという単一の環境関連情報の階層データベースの中に内蔵されています。Unixで例えるといくつもあるconfファイルがひとつにまとまっている状況に近いと思われます。データが集中していることから、このレジストリの情報の書き換えを失敗すると、場合によってはOS自体が起動してこなくなる可能性があるので操作についてはおすすめできません。よく言われる自分の足を撃つのWindows管理者バージョンです。ただやはり確認をしなければならないというときは、以下のコマンドで実現できるとみられます。

```
Get-ChildItem env:
```

### WMIを利用したハードウェアリソースの確認

Windows APIにはWMI: Windows Management Instrumentationというハードウェアからソフトウェアに至るコンピュータリソースへのいろんなものにアクセスするためのAPI(広範囲すぎてこうした表現しかできませんでした)が存在し、基本的にここを扱うことで比較的似たようなオブジェクト操作を実現できます。今回はこの中でOSやCPU、メモリなどを中心に取り扱います。

参考: WMI のアーキテクチャ
https://docs.microsoft.com/ja-jp/windows/win32/wmisdk/wmi-architecture

#### WMIオブジェクトの詳細な表示方法

後述するOSのコマンドでは、一部のプロパティが見えなくなっていたりします。それら全てを確認したいときは以下のようにパイプラインを利用して記述することですべて表示させることができます。

```
Get-WmiObject win32_operatingsystem | Select-Object *
```

またすでに目当てのオブジェクトがある場合は以下のコマンドのようにオブジェクトを指定して出すこともできます。

```
Get-WmiObject win32_operatingsystem | Select-Object caption
```

#### OSの確認のコマンド

```
Get-WmiObject win32_operatingsystem
```

#### CPUの確認コマンド

```
 Get-WmiObject win32_processor
```

#### メモリの確認コマンド

```
Get-WmiObject win32_physicalmemory
```

#### ストレージ(ディスクドライブ)の確認コマンド

```
Get-WmiObject win32_diskdrive
```


## ここから先の対象読者
Windows APIの理解の前提となる、よりシンプルなUnixをシステムコールを利用して操作してみたことがある、というレベルで扱っていることを前提とした記述をしていきます。たとえばすべてはファイルであるなどの前提の上で書いてしまいます。以下の記事でそれに関する詳細を記述しておりますので、必要に応じてご確認ください。なぜWindowsを知るためにUnixも知っていなければならないのか？私も正直わかりませんがインサイドWindows上のAmazonでのサンプルを見た限りだとUnix関連の例えが多かったので多分避けられないんだと思います……

https://qiita.com/GCkurabe/items/6b1e2fa144e1cc8851f6

UnixShellのことも比較対象として扱ってしまうので、ある程度触ったことがあると良いと思われます。比較的簡単な利用方法に関しては以下の記事が参考になると思います。

https://qiita.com/GCkurabe/items/16cf5d6299e36f99ac84

それと比較的簡単なオブジェクト指向のプログラムの理解はあったほうが良いと思います。Javaのような厳密なものというよりは、PythonやJavaScriptなどでの多少の知識があればよいくらいです。



## UnixShellとPowerShellの違い
UnixShellとPowerShellの違いは、ファイルを前提に扱うか変数(オブジェクト)を前提に扱うかにあります。


### ソフトウェアの扱う記憶領域: ファイルと変数の違い

プログラムコード内では、さまざまな記憶領域を取り扱っています。その記憶領域は非常に大まかに分けて二種類あると考えています。

- 変数
- ファイル

#### 変数

変数はプログラム実行されプロセスとなったときにその内部にのみ存在する、揮発的な記憶領域です。

ここでの変数の定義には配列やリストや辞書、それらを束ねた構造体変数やオブジェクトのインスタンスが該当します。

つまりプロセスが終了すれば、変数は消えることとなり、永続化できません。同時に、他のプロセスに変数の値を渡すことは原則できないことととなります。これを回避するには、複数のプロセスを統括するひとつのプロセスを準備し、それぞれにシェルスクリプトやRPCなどで値を渡していくというやりかたになるでしょう。または共有メモリなどで無理やりプロセス同士で繋いだ変数のやりとりをするとか、マルチスレッドによって擬似的に複数のプロセスがいるようにみなすしかありません。しかしこれでも実行結果を画面に表示しないと結果を残すことができません。

#### ファイル

そこで、変数の結果を永続化させる記憶領域としてOSもといカーネルが提供するファイルが利用できます。実行結果を変数に書き込んだあと、変数からファイルに書き出すことをすれば、プロセスが終了してもファイルの中身を確認することで記憶領域を永続化させられます。



### UnixShellはファイルを前提とする理由

UnixはIPC:プロセス間通信をファイルを前提として取り扱います。

具体的にシステムコールの観点からいくと、標準入出力に始まり、パイプからソケットまで、ファイルディスクリプターによって表現可能なようにデザインされています。

かつ、Unixのユーティリティソフトとなるlsなどのほぼすべてコマンドの出力は、常にテキストファイル化可能なバイトストリームとなっています。

このため標準入出力の内容をリダイレクトしてファイルに書き出したとしても、可読性を維持したままにすることができます。



### PowerShellは変数を前提とする理由

PowerShellの一般的な基盤となるWindows NT系は、IPCのほとんどを変数を前提として取り扱っています。

Windowsはシステムコールがない、というわけではないのですが、ほとんど操作方法はWindows APIと呼ばれるインターフェースを経由することが前提となっているようで、実際にUnixのシステムコールとWindows APIを比較すると(比較する対象として正しいかはさておいて)PowerShellのユーティリティコマンドから触った感覚としては多機能でかゆいところに手が届く感があります。Pythonで例えるなら、事前にライブラリやらモジュールやらが標準で大量にインポートされててすぐ使える感じです。lsコマンドなどのUnixのユーティリティコマンドみたいな感じ、とすぐ出てこないのは後述するPowerShellの特徴が影響していそうではあります。

このWindows APIの正体は大量のDLL: Dynamic Linking Libraryで、これらを抽象化するためにいろんな名称のものが存在しています。NT DLL, DLL SV, COM: Common Object Model, COMサーバーは全てDLLに関連する話をしています。

このAPIの巨大さをLinuxのシステムコール数となる数百の単位と比較すると、一桁多い数千に及ぶとされています。これだけのAPIが存在するのは、Microsoftがカーネル以外のOSと呼べる全てを実装していることにあります。たとえばウィンドウシステムも別の団体が作っているわけでもないですし、ExcelからPowerPointでコピペが可能になってたりするのもMicrosoftによるものです。AppleのiOSやMacOSでもよくあることですが、アップデートなどがあると一部不具合が確認されてしまうことがあるのはこうしてひとつの企業が膨大なAPIに対して常にメンテをしなければならない、かつ納期もそれに合わせて非常に厳しいところがあるのかもしれません。

かつ、PowerShellのユーティリティソフトとなるGet-...などのほぼ全てのコマンドも、常にWindows APIを剥き出しで利用するかのように、常に変数、というよりはオブジェクトを返してきます。これを可能にしているのは、.NET FrameWorkのオブジェクト・モデルをベースとしているからのようです。.NET(およびWindowsの大規模なDLL関連の仕様)に詳しい人ならば、より深いPowerShellの利用方法を確立できるかもしれません。

参考: Windows PowerShellのパワーの源は.NETオブジェクト

https://atmarkit.itmedia.co.jp/ait/articles/0607/26/news118.html


おかげでdirを打ってgrep代わりにsls: Select-Stringsコマンドでパイプを挟んで使っても文字列やファイルじゃなくオブジェクトを確認してしまうのでよくわからない挙動をする、ということが起きます。以下のもディレクトリはありそうですが普段の見え方と違うような気がします。

```
PS C:\Users\kurawo> dir | sls Desktop
Desktop
```

UnixShell風に一行丸ごと出すなどのように扱いたいなら、oss: Out-String -Streamを挟むしかありません。

```
PS C:\Users\kurawo> dir | oss | sls Desktop

d-r---        2021/06/05     23:13                Desktop 
```

以下の記事を参考にさせていただきました。いまも便利に使わせてもらっています。ありがとうございます。

参考: Out-Stringを使ってSelect-Stringをより便利に使う

https://blog.shibata.tech/entry/2015/08/23/235304


郷に従うのならパイプを無理に扱わず、以下のようにdirのコマンドラインオプションを利用してしまう方がよいです。

```
dir desk*
    ディレクトリ: C:\Users\kurawo
Mode                 LastWriteTime         Length Name                                                                                                                                                                                           
----                 -------------         ------ ----                                                                                                                                                                                           
d-r---        2021/06/05     23:13                Desktop
```

以上の理由から、PowerShellは変数(オブジェクト)を前提としないとむしろ運用が難しいです。個人的には、Pythonのようなオブジェクトを扱うインタプリタ言語にファイルの入出力機能がまだ多少UnixShellのような名残がある、くらいの感覚で新しく覚えるくらいで始めてしまったほうが精神的にも良かったです。

また、インタプリタ言語に関わる話ですが、curlのように利用できるものとしてInvoke-WebRequestやInvoke-RestMethodというものがあったりもするのですが、ソケットすら作れてしまう場合もあります。テストで繋いでみたりするときで、手元にWindowsしかない時にでもPythonのように便利に使えるかもしれません。

Invoke-WebRequestやInvoke-RestMethodの参考: PowerShellで HTTPアクセスする いくつかの方法

https://qiita.com/kurukurupapa@github/items/c77b7be7f3c05453e75e

ソケットの参考: telnetコマンドのないWindowsでのTCPポート確認

https://qiita.com/_norin_/items/8f534bd0531a960af5e9


## おわりに

PowerShellでのリソース確認という簡単なコマンドに始まり、PowerShellの仕様も理解することができました。

またWindowsにはChocolateryというMacOSでいうところのHomebrewやUbuntuのaptに相当するパッケージ管理システムがあり、こちらもうまく活用することでセットアップ関連を手早くできる可能性を秘めています。PowerShellはChocolateryを使っているときはただ単なるCLIにしかならないかもしれませんが、Windows CLIの利用用途を広げるという意味では良いのかもしれません。

https://chocolatey.org/

PowerShellは理解さえできればかなり自由度が広がった感覚が得られるので、引き続きUnixと共に何かを知っていけたらと思っています。