Power shell を使おうぜ☆（＾～＾）＜その１＞

## 見たことがない人もいる

会社のWindows PCは、PCでできること　に制限がかかっていて、
自宅PC にフリーソフトをインストールして　バリバリ使っている人に比べて
デフォルトに近い状態の PC しか見たことがない人もいる。
それも 隣の人のPCも、客先のPCも制限がかかっているのだから、世界中のPCがそんなもんだと思うのも無理ない。

逆に　わたしは　制限がかかっている PC があることを知らず、
UAC（ユーザーアクセス制御） が出てきて .bat ファイルが実行できないPCが
あることを知らず runas したり、権限を昇格させたり　いろいろするはめになる。

グローバルポリシーを設定できる管理者と、制限のかかったPCの中間ぐらいに 開発部 があったりする。
せっかく作ったスクリプトが　他のＰＣでは動かない、という　制限　を頭に入れてから　取り組んでほしい。

## マルチ プラットフォーム

Microsoft は Power shell に .bat を オーバーラップ（そのまま使えるようにする意味だが 習慣が異なる部分でつまづくのは自力で乗り越えろ）させ、
.NET Framework をマルチ プラットフォームに書き直した .NET Core に差し替えたことで
Windowsユーザーを Linux にもそのまま　持っていけるように狙っているようだ……、何かの記事で読んだ。

Node.jsを使ってマルチプラットフォームで動くように Webサーバーを作っても
Windows Server 2016 で OSのリスタート時に タスク スケジューラーから .bat を叩く部分だけ Windows/Linux で切り分けていて　めんどうだったので .ps1 に書き直すことを試そうとしたりする。
本当にうまくいくのか　知らないが……☆（＾～＾）

## ダブルクリックして起動しようぜ

[2015年 12月 24日 PowerShell スクリプトファイル(.ps1)をダブルクリックしても実行されないとお嘆きのあなたへ](https://devadjust.exblog.jp/22696383/)

Windows 10 を使っているとする。
Power shell の拡張子は `.ps1` なんで、 `hello-world.ps1` という空ファイルを作って
ダブルクリックしてみろだぜ☆（＾～＾）

メモ帳で　空のテキストファイル　が開くだろう。
こんなことでは　Power shell を使い始めようという気にはならない。

[Windows] + [R] と打鍵して `regedit` を検索しろだぜ。
レジストリーエディターが出てくる。

```Plain
v コンピューター
  v HKEY_CLASSES_ROOT
    v Microsoft.PowerShellScript.1
      v Shell
        v 0
        v Edit
        v Open
```

というのがあることを目視確認する。

`0` をクリックすると

|名前|種類|データ|
|---|---|---|
|MUIVerb|REG_EXPAND_SZ|`@"%systemroot%\system32\windowspowershell\v1.0\powershell.exe",-108`|

というのがあれば、 PowerShell のバージョン1.0 が使えることが分かる。古い……。

次は `Shell` をクリックし、

|名前|種類|データ|
|---|---|---|
|(規定)|REG_SZ|Open|

名前の `(規定)` をダブルクリック。 [値のデータ(v)]テキストボックスに `Open` と入っているが、
`0` に書き換えて[OK]をクリックしろだぜ。

レジストリーエディターは閉じろだぜ。

`hello-world.ps1` ファイルをダブルクリックしてみろだぜ☆（＾～＾）
今度は Power Shell が起動する。

## Windows Power Shell ISE を使おうぜ

Visual Studio Code にも Power shell エクステンションはあるんだが、使いやすい方でいいだろう。

Windows 10 には最初から アイス(ISE) が入っている。
Power Shell を使う、と決めたら アイス は普段使うソフトになるので、
タスクバーにピン留めしておくといいだろう。

バッチ(.bat)ファイルのように、コマンドプロンプトを開いて打鍵して……、とか　やらなくなる。
アイスをずっと開いておく。

ローカルPCでは ユーザー・ホームに最初居る。

```Shell
PS C:\Users\むずでょ>
```

バーチャル ホストでも ユーザー・ホームに最初居る。

```Shell
PS X:\>
```

とりあえず 知らないドライブ にいたら社内環境依存だ、**ググっても出てこない**。知ってるドライブ に行けだぜ。

```Shell
# スクリプトの置いてあるディレクトリへ移動
PS C:\Users\むずでょ> cd $PSScriptRoot

# 現在のディレクトリを確認
PS C:\Users\むずでょ> pwd

Path
----
PS C:\Users\むずでょ
```

## 実行権限を設定しようぜ

* [[Power Shell] Power Shellでスクリプトファイルを実行できるようにする](http://windows-podcast.com/sundayprogrammer/archives/95)
* [about_Execution_Policies](https://docs.microsoft.com/ja-jp/previous-versions/windows/powershell-scripting/hh847748(v=wps.640))

アイスが使えるようになったら、使っていこう。

アイスは、 アイコンをダブルクリックするのではなく、右クリックして [管理者として実行(A)] を使って実行する。

```Shell
PS C:\Users\むずでょ> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine       Undefined

PS C:\Users\むずでょ> Get-ExecutionPolicy
Restricted
```

カレントユーザーの実行ポリシーは未設定、わたしは制限されている。
まずは制限を解除したい。

```Shell
# 確認ダイアログが出てくるので [はい] で進める。
PS C:\Windows\system32> Set-ExecutionPolicy RemoteSigned

PS C:\Windows\system32> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine    RemoteSigned
```

これで、ローカルＰＣの Power shell のスクリプトは証明書なしで実行できるし、
メールなどインターネットからダウンロードした Power shell のスクリプトは 証明書あり なら
実行できるようだぜ。

じゃあ アイスは一回閉じろ。管理者権限なしで開き直せだぜ。

## Path にくせがある

[Copy-Item -Recurse の振舞](http://www.vwnet.jp/Windows/PowerShell/copy/Copy-Recurse.htm)

```Plain
C:\
    
```

パソコンには、フロッピーディスクを差し込む穴である Aドライブ、Bドライブ があり、
ハードディスクを内蔵している高価なPCは ハードディスクが Cドライブ だ、
という意味でドライブは C から始まるが、
バーチャルホストも当たり前になってくると　URL　の方でやりたくなる。
ドライブが頭にあっても　同じ　アルファベット　で始まるが　ホストが違う、とか出てきて　状況説明がめんどくさい。

この パス を最短ステップでどう説明できるだろうかと、現場で 1分 ぐらいで実践するはめになることがある。
**バッチファイルは　ローカルＰＣ　で実行される**、という説明をするために
ローカルＰＣとは何か、ＰＣだと思っている「鉄のケース」の中には何が入っているのか、
ドライブとは　円盤が回転することであり……、と　ろくろ　を回すジェスチャーをする人になる。
ヒマがあれば `UNC` も自習しろだぜ。頭に `\\` が付いている、ネットワークにつながっている Windows でよく見かけるやつだぜ。

```Plain
C:\
    apple\
        banana.txt
```

という階層で、ファイルがあるとする。Power shell では、

```Plain
C:\apple
```

と

```Plain
C:\apple\
```

では意味が違うケースと出会う。
URLでは、末尾に \ が付いていようと、付いていまいと　ブラウザが判別してくれるが、
Power shell では
ディレクトリーをコピーしようとしたとき、
宛先のディレクトリを置き換えるのか、宛先のディレクトリの中にコピーするのか、
ぐらいの違いが出てくる。

８パターンぐらいあって　暗記するのも面倒なので
動きがおかしいと思ったら　その場で　動作確認しよう。

次の記事にわたしの考えをまとめておいた。
[何かだぜだぜ言っているタイトルがめんどくさい（略）](https://qiita.com/muzudho1/items/6a8ac44c9418457d9029)

# コンパイラの考え方が役に立つ

Power shell が有ったって、仕事がなければ　何の足しにもならないし、
しかも　Power shell が役に立つような　仕事　でなければ　何の足しにもならない。

仕事の方を　Power shell　に合わせるなんて　本末転倒　ではないか、というと
Power shell　に合わせた仕事は　全自動化　されるんで　末節自動　であり　正しいPCとの付き合い方だぜ。

良い例も無いんで　とりあえず　手順書を書いて、１つにまとめる　状況をやってみる。
いまどき Git Hub を使って README.md を書くのも慣れてるだろ。
Markdown（マークダウン）形式で手順書を書こう。というか Qiita も Markdown だ。

Visual Studio Code で Market Place から `Markdown All in One` をインストールしているものとする。
[Ctrl]+[Shift]+[P] で `Markdown: Print current document to HTML` を選ぶと、同じディレクトリに HTML ファイルが自動的に出力されるようにしておけだぜ。

（というか VS code に Power shell をインストールしていれば、 [Ctrl]+[Shift]+[`] で Power shell が起動する）

```Plain
C:\kifuwarabe\
    README.md
```

仮にこんなディレクトリ構成とする。ここで わざわざ、

```Plain
C:\kifuwarabe\
    -- META\
        kifuwarabe-doc\
            img\
                icon\
                    folder-small.png
            README.html
            README.md
        kifuwarabe-doc#make.ps1
```

というフォルダー構成に変換し、 `kifuwarabe-doc#make.ps1` をダブルクリックすると

```Plain
C:\kifuwarabe\
    -- META\
        kifuwarabe-doc\
            img\
                icon\
                    folder-small.png
            how-to-setup.html
            how-to-setup.md
            README.html
            README.md
    docs\
        kifuwarabe-doc\
            img\
                icon\
                    folder-small.png
            how-to-setup.html
    README.html
    README.md    
```

のように展開するプログラムを考えてみる。
.md ファイル以外をディレクトリごとコピーして、README だけ外に出す形だ。

「同じものをコピーするだけ　なんて　つまらない」　かもしれないが、
手順書というものが　「**同じものをコピーするだけ**」　の場面がいかに多いかと言えば
賢明な読者諸君なら　HTMLには　ハイパーリンク　があることを思い出すと
大元を一元化し、コピー配布を自動化することが　いかに便利か　端々まで一瞬ですべて見通せたと思う。

つまり、

```Plain
C:\
    kifuwarabe\
        -- META\
            kifuwarabe-doc\
    task-scheduler\
        -- META\
            task-scheduler-doc\
    microsoft-apps\
        -- META\
            microsoft-apps-doc\
```

のように 手順書の大元を 物別 に作っておき、あとは Power shell を使って

```Plain
C:\kifuwarabe\
    docs\
        kifuwarabe-doc\
        task-scheduler-doc\
        microsoft-apps-doc\
```

のように１つに　まとめて、ハイパーリンクでつなげだぜ。

「**フォルダー数が多い！**」「**１つのファイルにまとめろ！**」という上司の下で働いていれば、転職しよう。転職先は　外資系か、零細企業がいいだろう。　日本の大企業、中小企業は　情シス　が作ったルールを守る側に回るだろうから、ルール作りから考えて作業する側には回れないだろう。

## スクリプトを書き始めよう

```Shell
cls
cd $PSScriptRoot
cd ..
pwd
```

こう書くと、.ps1 ファイルがあった上のディレクトリに移動する。じゃあ、次のように書けば……。

```Shell
cls
cd $PSScriptRoot
cd ..
pwd

# Trailing slash.
$product = (Get-Location).Path + "\"
$original = $product + "-- META\kifuwarabe-doc\"

# Trailing NOT slash.
$deployment = $product + "docs\kifuwarabe-doc"

echo "Product: $product"
echo "Original: $original"
echo "Deployment: $deployment"
```

まだ説明していない書き方もバンバン使う。次のような出力になるはず。

```Shell
Path                                           
----                                           
C:kifuwarabe
Product: C:\kifuwarabe\
Original: C:\kifuwarabe\-- META\kifuwarabe-doc\
Deployment: C:\kifuwarabe\docs\kifuwarabe-doc
```

ここでファイルパスが間違っていると、ディレクトリを消し飛ばしたり、知らないところにディレクトリが作られたりして　まずい。よく目視確認しよう。
ここで、**末尾に** スラッシュ `\` が付いているか、付いていないかで結果が異なる。

ひとまず、 `$original` が示すディレクトリを、 `$deployment` に丸ごとコピーすることを考えよう。

```Shell
Copy-Item $original $deployment -Recurse
```

これで丸ごとコピーはやってくれるが、もう１回実行したときに、「既に存在します」とか言われて　失敗してしまう。
何度でも実行できるように リサイクル（Recycle）な形にしておきたい。

リサイクルとは　捨てたものを再び使うこと　**ではなく**、何度でも捨てれることだ。
そのために必要なことは、 `$deployment` ディレクトリの中には　捨てられて困るものは置かないことだ。
それを実現するためには `docs` ディレクトリは リサイクルさせられます、というルール、習慣を周知、徹底させることが必要だが、
誰だって、知らなければ docs ディレクトリの中のファイルを編集して、いつの間にかオートマティックに消されてしまう。
ルール、習慣の周知徹底という手段は　悲観的に考えて、実現できない。できないことを、やろうと言うのは、ゲームが下手なプレイヤーだ。

それは置いておいて　ひとまず　さっきのコードの頭に、

```Shell
# Recycle.
if (Test-Path $deployment) {
    Remove-Item $deployment -Recurse
}

Copy-Item $original $deployment -Recurse
```

ディレクトリがあれば、そのディレクトリを消すという３行を追加しておけば、このプログラムは何度でも実行できるようになる。

* kifuwarabe-doc.ps1 ファイルを別のディレクトリに移動したり
* $deployment ディレクトリをそもそも間違えて指定していたり

しなければ無事に動く。ところで　ゲームが上手いプレイヤーなら、

* 「有料」というラベルを貼っておく
* プラス・マイナスのドライバーで開けられないネジ穴をしている
* 信じられやすい噂話を広めて、真実は隠す

など　心理的なテクニックで　場を制圧するので、

```Plain
exec\
    Windows-System.bat
    E012X201U931.call
    X1909a91c190[8e23]000d#ewoa.ewio.ada
```

ディレクトリ名も docs を止めて exec にしたり、
怪しげな　空ファイル　を置いておくのも手だろう。中身には般若心経でもランダム生成しておけばいい。

ただ、プロジェクトによっては どのディレクトリにどのようなファイルが置いてあって パーミッションの設定まで必要なこともあるので　この手法は一般的には使えない。

```Plain
docs\
最初に読んでください.html
```

また、docs のディレクトリの外に `最初に読んでください.html` を置いておく。
README.html という名前のファイルをダブルクリックする習慣はない。
しかし `最初に読んでください.html` でも 読む習慣はない。
そもそも　ドキュメントを読む習慣がない。
そして「ドキュメントは読まないから必要はない」の半年後に「今すぐドキュメントをください」のミドルレンジ連打がくるので、実装をドキュメント化するというより、概要だけ　かいつまんだ　メモ書き　を入れておくと　半年後に助かる。

### フィルターで ファイルを選り分けよう

ただ、今のままだと　すべてのファイルを丸ごとコピーしてしまうので、 `-Exclude` を使って コピーしなくていいファイルを 拡張子 で弾こう。

```Shell
$excludeArray = @("*.md", "*.pu")
Copy-Item $original $deployment -Recurse -Exclude $excludeArray
```

例えば 上記のように書けば 拡張子が .md と、 .pu のファイルはコピーされない。
-Include もあるらしいが、動きが思ったのと違ったので -Exclude ばっかり使っている。

## １度書いたプログラムは使い回そうぜ。

プログラムに慣れていると、１度書いたプログラムは、使いまわしたくなる。
Power shell にも function という機能がある。

```Shell
<#
 # さっき書いたプログラムを、function という書き方にしておく。
 #>
<#
 # ドキュメント作成。
 # $pro - product.
 # $ori - original.
 # $dep - deployment.
 #>
function Copy-Doc($pro, $ori, $dep) {
    # Recycle.
    if (Test-Path $dep) {
        Remove-Item $dep -Recurse
    }

    $excludeArray = @("*.md", "*.pu")
    Copy-Item $ori $dep -Recurse -Exclude $excludeArray
}

# 一度書いた function を使っている。
Copy-Doc $product $original $deployment
```

他のプログラム言語と違って Power shell は コマンドラインなんで、丸かっこを使ったり、カンマで区切ったりしない。

## いくつものプログラムから使われるプログラムは、別ファイルにしようぜ。

[Powershell > スクリプトを複数ファイルで実装する方法 > .(ドット)で別スクリプトを読込む](https://qiita.com/7of9/items/50554f422404471bcfb8)

さっきの function を切り抜いた、新しいファイルを作る。

```Plain
C:\kifuwarabe\
    -- META\
        document.ps1
```

Power shell 自体、オブジェクト指向ではないので ファイル名の付け方がむずかしいところだ。

```Shell:document.ps1
<#
 # ドキュメント作成。
 # $pro - product.
 # $ori - original.
 # $dep - deployment. Trailing NOT slash.
 #>
function Copy-Doc($pro, $ori, $dep) {
    # Recycle.
    if (Test-Path $dep) {
        Remove-Item $dep -Recurse
    }

    $excludeArray = @("*.md", "*.pu")
    Copy-Item $ori $dep -Recurse -Exclude $excludeArray
}
```

切り分けたので、もう片方の .ps1 は、

```Shell:kifuwarabe-doc#make.ps1
cls
cd $PSScriptRoot
cd ..
pwd

# Trailing slash.
$product = (Get-Location).Path + "\"
$original = $product + "-- META\kifuwarabe-doc\"

# Trailing NOT slash.
$deployment = $product + "docs\kifuwarabe-doc"

echo "Product: $product"
echo "Original: $original"
echo "Deployment: $deployment"

. "-- META\document.ps1"
Copy-Doc $product $original $deployment
```

読みやすくなった。
同じプログラムは　何度もコピー貼り付けするより、
１つのライブラリにして　色々なプログラムから　使い回す方が、
バグを見つける機会が増えるし、修正アップデートしていけば　どんどん　不具合が減っていく。

大企業のプロジェクト仕事では　こういう知見は相手にされないから、早く転職しよう。

／／　書きかけ