Powershell Scriptの書く順番の定石 暫定版

# シンプルなスクリプトを対象
基本的にファイル`ps1`で作業が終了するようなスクリプトを対象とする。
リモート、まあ関係ない。実際はこれが多いのかもしれないが。
アプリケーションなどを呼び出したり、Officeのファイルを読み込んだりしても、他のPS1を呼び出すことはない、というシンプルなもの。

# 書く順番
## 概要
 1. Using
 2. 関数
 3. 変数 ユーザー
 4. 変数 （定数的なもの）
 5. エラー処理等の設定
 5. New-Object系
 6. ***メイン（ここで処理をする）***
 7. 変数の開放等の処理

記載する順番は基本はこのようになる。
このあとにデータセクションと言われる、独自のコメントやヘルプを追加することができる。
データセクションについては触れないので、あとに記載しているステートメントを参照し、データセクションを参照すること。

## 基本的に最初から読んでいるらしい
Powerhsellは関数などが後ろにあるとエラーになる。
Usingは最初に必要。
関数など後から使うものを先に読み込んみ、メインがきたら作業を開始するというフローになっているらしい。
これは、関数等を呼び出すには先に出現してなければならないというルールがら推察される。

## Using句があれば最初に書く
[Using句について](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_using?view=powershell-7.3)
`.Net`名前空間、つまり名前付き命令集合を使役する宣言。
このため最上位、つまり最初に書く。
この using ステートメントを使用すると、セッションで使用する名前空間を指定できまる。 名前空間を追加すると、.NET クラスとメンバーの使用が簡単になり、スクリプト モジュールやアセンブリからクラスをインポートできまる。

> ステートメントは using 、スクリプトまたはモジュール内の他のステートメントの前に存在する必要があります。 パラメーターを含め、コメントアウトステートメントの前に記述することはできません。
 ステートメントに using 変数を含めてはなりません。
 ステートメントは using 、変数の using: スコープ修飾子と混同しないでください。 詳細については、「 about_Remote_Variables」を参照してください。

なお、Using句で召喚しているのは.Netの機能が多い。Powershellはこの機能を使うと遅くなる。
API Broser
https://learn.microsoft.com/ja-jp/dotnet/api/?view=netframework-4.6.2
このページの一覧が、概ねUsingのはずである。現在7まで行っているが臨時に読み込むのは4のあたりと思われる。
なお[PowerShell スクリプトのパフォーマンスに関する考慮事項](https://learn.microsoft.com/ja-jp/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations?view=powershell-7.3)によるとUsingを使うと遅くなるので、Powershellにない機能だけ呼び出したほうが良いとのこと。また、すべてをUsingで呼び出す必要はなく、New-Objectで呼び出す方法をつかても良い。

## ユーザー定義関数が必要であればUsingの次に書く
Powershellでは関数が第2順位です。なぜなら呼び出される前に記載する必要があるから。
[関数について](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-7.3)

## Main
メインであるというコメント

```powershell
## Main ##
# Set-Executionpolicy RemoteSigned -Scope Process -Forse
# 機能説明、使用方法、注意事項
```

## Add-Type（Ver5.1）
[Add-Type](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.3)
まだ読み込まれていない場合は最初に書くので最初
Powershell 6 以降は不要とされているが使用は許可されているが、基本的に使用しないほうがいい。

Powershellはこういうマイルールが多すぎ。
また、Add-TypeとUsingは矛盾するケースがある。
[PowerShell_ISE で 書いたスクリプト がpowershell で実行したときに失敗する](https://teratail.com/questions/174707)


## ユーザー定義変数
スクリプトを実行するときにユーザーが値を定義する。

```powershell
## ユーザー定義変数 User Definition variable
## シングルクォーテーションで囲んでください
[Stiring]$userFoler = 'C:\hoge'  # ログを記録するフォルダ
```

このようにユーザーが定義する変数を記載する。データ型を明示したほうがいいと思われる。

## 基本設定変数
[about_Preference_Variables](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-5.1)
エラー処理の定義等を上書きする。これはセッション、つまりスクリプト内に限り有効。

:::note warining
ユーザー設定変数の変更は、それらのスクリプトまたは関数が、ユーザー設定が使用されたスコープと同じスコープで定義されている場合にのみ、スクリプトと関数で有効になります。 詳細については、「 about_Scopes」を参照してください。
:::

## 特殊文字の定義 改行、タブ

[about_Special_Characters](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-7.3)
特に改行　vbCrLf はわけがわからないので次のように定義する。
VBをやっていればわかりやすい。
この他色を使う場合には色の定義をすると良い。

```powershell
$vbTab='`t'; $vbCrLf= '`n`r'
```
## New-Object系

[.NET オブジェクトと COM オブジェクトを作成する (New-Object)](https://learn.microsoft.com/ja-jp/powershell/scripting/samples/creating-.net-and-com-objects--new-object-?view=powershell-7.3)

```Powershell
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObject
```

このようにWSH系のオブジェクトも呼び出せる。
しかし、このようにUsingかNew-Objectなのかはっきりしない。

## Main

ここまで定義だけで100行くらいは使う。
やっと本体である。
Powershellでコードを書かない最大の理由は可読性にかけている点であろう。
ワンライナーなどは強いが、Scriptをきれいに書くのは難しく、構造上、メインがあとに来るようになる。
更にここから通常は
Script blockというよりTry...Cathchでプロセスごとに囲む。
最低これだけでもスクリプトの可読性が上がる。

```Powershell
## Excel.Application
Try{
}Catch{
Echo 'Line 39' + $Error[0]
Exit
}

## Read CSV File
### File Check
If(Test-Path ){

} # Line 43 If
### Read
Try{
}Catch{
}
##
```

また、`If`、`Do`、`For`、`While`は最後に`　# 何行目のIf`とか`# If(Test-Path...`と具体的な内容を`}`の後ろにコメントを入れる。また、途中のCatchは変数を開放する。またエラーを表示させて終了させる。

## 変数の解放、削除、破棄
Powershellの中で一番分かりづらいのは変数の解放だろう。
例えばOffice系統はファイルを閉じる。Accessの場合はレコードを閉じる、接続を閉じる。
というのを行い、ExcelはQuit()を使う。
[PowerShellでCOMオブジェクトを捨てるときの方法をまちがえていた件 ](https://note.com/nalulabo/n/nab227629d8b0)
[[powershell5]必要のなくなった変数を開放(消去)](https://qiita.com/kiduki/items/13b6091ee27a4a91e0c3)
一応セッション内の変数の一覧は

```Powershell
Get-Variable .
```

で取得できる。[変数について](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_variables?source=recommendations&view=powershell-7.3#working-with-variables)参照
しかし、Rmove-Variableはあまり使われない。
[関数内でのリソース解放処理](https://winscript.jp/powershell/309)
[PowerShell再入門：7. COMを使う](https://www.tumblr.com/m0t0k1x2/121646347059/powershell%E5%86%8D%E5%85%A5%E9%96%807-com%E3%82%92%E4%BD%BF%E3%81%86)

# 関連
## ポータルの問題
[PowerShell ドキュメント - PowerShell の公式製品ドキュメント](https://learn.microsoft.com/ja-jp/powershell/?view=ps-modules)
　[サンプルのスクリプト](https://learn.microsoft.com/ja-jp/powershell/scripting/samples/sample-scripts-for-administration)

一応これがトップページで、これがサンプルスクリプトだが、マイクロソフトの連中はどうもトップページから下に行くこと以外知らないらしく、下から上がるともとには戻れない。
###その中で
[Windows PowerShell 言語仕様 3.0](https://learn.microsoft.com/ja-jp/powershell/scripting/lang-spec/chapter-01?view=powershell-7.3)
URLを見ると、3.0と言いながらしっかり7.3のバージョンとなっているので記述は更新されているようだ。
こちらはWord文書もダウンロードセンターから入手できる。
[Powershell101](https://learn.microsoft.com/ja-jp/powershell/scripting/learn/ps101/00-introduction?view=powershell-7.3)もあるが、こちらは7.3で紹介されていても、5.1のインストールから始まる。完全に5.1で記述され、更新されている気配がしないので記述が合わないかもしれない。こちらは電子洋書があるようだ。

## 破壊的変更の問題
[PowerShell 3.0での破壊的変更まとめ](https://blog.shibata.tech/entry/2018/12/01/113228)
[PowerShell Core 6.0 破壊的変更まとめ](https://blog.shibata.tech/entry/2018/02/11/190420)
[Windows PowerShell 5.1 と PowerShell 7.x の相違点](https://learn.microsoft.com/ja-jp/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.3)
このため、うかつにネット上のサンプルを使えない。
特にAdd-Type系を使ったサンプルは要注意。

[PowerShellの"罠"と呼ばれるモノについて](https://blog.shibata.tech/entry/2018/05/31/214439)
> リダイレクト演算子やファイル操作に関わるコマンドレットの既定のエンコーディングがBOM付きUTF-16であることやUTF-8がBOM付きなのは、基本的には.NET Frameworkにおけるエンコーディングがそうである影響です。

Powershellでは文字コードを意識しなければならない。

[PowerShell Gives You Wrongs](https://gist.github.com/igeta/e5c0d8e81c732d55a919b97cbdaf459a)
2.0から3.0は書き換えろが対策... :frowning2:
Nullの扱いがポイント。
スクリプトブロックのIIF的な書き方（IF）は参考になる。

## 今回は扱わなかったリモート
[about_Session_Configurations](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_session_configurations?view=powershell-7.3)
> セッション構成 ("エンドポイント" とも呼ばれます) は、ローカル コンピューター上の設定のグループであり、リモート またはローカル ユーザーがローカル コンピューター上の PowerShell に接続するときに作成される PowerShell セッションの環境を定義します。

簡単にいうとリモート端末のPowershellに接続する設定。
またMacOS、Linuxでも動くと言っているが、WindowsだけでMacは動かないとかある。中途半端。

## 今回
[Scriptについて(7.3)](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_scripts?view=powershell-7.3)
[about_Scopes](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.3)
スコープとは照準のようにイメージしますが、日本語では「変数有効範囲」とでもすべきでしょう。

## スクリプトブロック
[about_Script_Blocks](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.3)
このスクリプトブロックは中括弧の中程度で意味がないように見えるが、変数の有効範囲（スコープ）が違う場合がある。またIIFのような表記がスクリプトブロックとか、変数の中身がスクリプトブロックとか、メチャクチャな使い方をする。簡単に言うとサブルーチンとか言われるもの。

## ステートメントブロック
[Powershell 言語仕様3.0 ](https://learn.microsoft.com/ja-jp/powershell/scripting/lang-spec/chapter-08?view=powershell-7.3)
スクリプトブロックと似ているが、VBAで言う行ラベルと似た機能を含んだものである。重要なのはあとのデータセクションやそこで定義する独自のコメントなどはこの8.9 data ステートメントによって定義する。

## Classの参照サイト
[超簡単な PowerShell Class の使い方(その6/まとめ)](http://www.vwnet.jp/Windows/PowerShell/2017082001/PSv5Class01.htm)～[超簡単な PowerShell Class の使い方(その6/まとめ)](https://www.vwnet.jp/Windows/PowerShell/2017082401/PSv5Class06.htm)
>荒っぽく言ってしまうと、Class は拡張された function です。



## Return
[about_Return](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_return?view=powershell-7.3)
Exit Sub Exit Function系。Classはリターンを書いたほうが良い
ただしPowershell　3.0から扱いが違う。Finally内では書けない。

## 例外が多すぎ
[例外について知りたかったことのすべて](https://learn.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-exceptions?view=powershell-7.3)
正直これを読んで理解できる人間はいない。例外はボールか何かか？Throwとかふざけてるわ。

## 変数のスコープ（鷲の巣）
[20181226 PowerShell のスコープ完全に理解した ](https://tech.blog.aerie.jp/entry/powershell-advent-calendar-2018-18)
とにかくPowershellかPowershell_iseを終了させれば解決する。

## 誰もが知りたいデータ型
[データ型](https://learn.microsoft.com/ja-jp/powershell/scripting/lang-spec/chapter-04?source=recommendations&view=powershell-7.3)
型とか意味のわからない翻訳である。
一般的に、Byte Boolean 小さい整数 大きい整数 十進、単精度、倍精度、 テキストがあるが、これ以外に参照型とがあり、データ型に入らないような概念が混ざっている状態。
なお、デフォルトがUTF-16であることがこの解説でわかる。ところが、Powershell6.0からはUTF-8だとかごちゃごちゃしている。
また、フォルダのことをコンテナと言い張るように、Booleanをスイッチ型と言い張るのがわからない。データーをデータに統一するくらいなら、BooleanはBooleanにすればいいのに。

## プロファイルとは環境変数に近い
[about_Profiles](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.3)
> PowerShell プロファイルを作成して、環境をカスタマイズし、開始するすべての PowerShell セッションにセッション固有の要素を追加できます。

Powershellを動かすときの設定。基本設定変数より恒久的。環境変数に近い。

## モジュール
[PowerShellにおけるモジュールの取扱い方](https://qiita.com/tomomoss/items/5f8c027f3bdc3b189791)
暗黙的ではないモジュールについては、使う前に書く。


## データ区画と言語で文章を切り替え（国際化）
[about_Script_Internationalization](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_script_internationalization?view=powershell-7.3)
[about_Data_Sections](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_data_sections?view=powershell-7.3)
この2つは関連が深い。例えば言語に合わせてメッセージを変えるという方法に使うことが想定されている。
Dataセクションだからデータと関係が深いわけでもない。これらは以下のコメントベースのヘルプと関係している。

[PowerShell スクリプトと関数のヘルプの記述](https://learn.microsoft.com/ja-jp/powershell/scripting/developer/help/writing-help-for-windows-powershell-scripts-and-functions?view=powershell-7.3)
/help/placing-comment-based-help-in-scripts?view=powershell-7.3)

 +  冒頭にもかけるとしているが、冒頭に書くとメインまでの距離が長くなる。
 +  [関数にコメント ベースのヘルプを配置する](https://learn.microsoft.com/ja-jp/powershell/scripting/developer/help/placing-comment-based-help-in-functions?view=powershell-7.3)
 +  [スクリプトにコメント ベースのヘルプを配置する](https://learn.microsoft.com/ja-jp/powershell/scripting/developer/help/placing-comment-based-help-in-scripts?view=powershell-7.3)

## 名指しで喧嘩を売るスタイル

[Invoke-Expression の使用を避ける](https://learn.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3)
> 2022/11/18
Invoke-Expression コマンドレットは、最後の手段 としてだけ使用してください。 多くのシナリオでは、より安全で堅牢な代替があります。 Stack Overflow などのフォーラムには、Invoke-Expression の誤用の例があります。 また、PSScriptAnalyzer にはこれに対する規則もあります。 詳細については、「AvoidUsingInvokeExpression」を参照してください。

> セキュリティへの影響を慎重に検討してください。 ユーザーによる入力などの信頼されていないソースから文字列を直接 Invoke-Expression に渡すと、任意のコマンドを実行できます。 常に、最初は別のより堅牢で安全なソリューションを検討してください。

ここまで名指しで喧嘩を売るのも珍しい。

## ポータルから読んだ人に特典
[PowerShell ユーティリティ モジュール](https://learn.microsoft.com/ja-jp/powershell/utility-modules/overview?view=ps-modules)
> PowerShell チームは、PowerShell の機能を拡張するユーティリティ モジュールのセットを作成しました。 これらのモジュールは、PowerShell ギャラリーからインストールし、必要に応じて読み込むことができます。 モジュールは正式には PowerShell 製品の一部ではありませんが、PowerShell 製品チームによって管理されています。 各モジュールのサポートレベルは異なります。 詳細については、各モジュールのランディング ページを参照してください。
このドキュメント セットには、次のモジュールのコマンドレット リファレンスが含まれています。

- Microsoft.PowerShell.Crescendo
- Microsoft.PowerShell.SecretManagement
- Microsoft.PowerShell.SecretStore
- PlatyPS
- PSScriptAnalyzer

個人で使っているならいいが、モジュールはインストールできないという環境も多い。このためモジュールは実際全くつかないし意味がない。と言うかそういうふうにしたのはマイクロソフトなのだが。