Powershell で別ファイルにあるスクリプト・関数を自作コマンドとして使いたい。そしてヘルプを表示したい。


# はじめに
べた書きのファイルをコマンドにして、さらにヘルプを付けるときに詰まったので書きました。

所々無理やり解決しているところがあります。

ここで言うスクリプトとは "関数になってない、処理がべた書きのモノ" を指します。  
(そういうのをなんていえばいいんでしょうか?どなたか教えてください!)

# 事前知識
## bash における .bashrc に当たるもの
自作コマンドを作るには、例えば Linux 等では ~~`.bashrc` に関数を書けばよい。~~    
(誤解を与える表現だったので訂正すると、Linux ではふつうは新しくファイルを作りパスを通すことが多い。ただし `.bashrc` に関数を書いても自作コマンドになる。)
<details><summary>Linux での例</summary>

<br>
<hr>
ファイルを作りパスを通す例

1. `~/commands/` に新しくファイルを作成する (例えば `hoge` (拡張子なし))
2. その中に以下のように記述する

```shell
#!/bin/sh

# repeat "hoge" for the given number
for ((i=1;i<=$1;i++));do echo "hoge"; done
```
<br>

`.bashrc` に関数として書く書き方

1. `.bashrc` に以下のように記述する
```shell
# .bashrc
hoge(){
        # repeat "hoge" for the given number
        for ((i=1;i<=$1;i++));do echo "hoge"; done
}
```
<hr>
</details>

PowerShell では例えば `\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`  などがそれにあたる。ただし複数種類があり、使い分けがされているようなので詳しくは以下を参照。

ちなみに私の環境では `echo $PROFILE` と打つと出てくる `\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` をいつも使っている。



https://qiita.com/smicle/items/0ca4e6ae14ea92000d18


## ドットソースによる別ファイルの展開

Powershell でもファイル分割をすることができ、 
```powershell
. "<ファイルパス>"	# <ドット> <スペース> <ファイルパス> の順
``` 

以上のようにすると、その場で同じコンテキストで別ファイルを展開することができる。
(ファイルパスに空白がない場合は引用符は不要だが、つけておくと安心)


## エイリアスの設定

Linuxと同様にコマンドに対して Powershell でもエイリアスを設定することができる。

```powershell
Set-Alias -Name <エイリアス名> -Value <実際のコマンド名・パス>

# 簡略版
sal <エイリアス名> <実際のコマンド名・パス>
```
`Set-Alias` のエイリアスは `sal` で、また `-Name`, `-Value` オプションは明示しなくてもいい

例)
```powershell
> Set-Alias -Name list -Value Get-ChildItem

# 簡略版
> sal list Get-ChildItem
```

# case 01: 別ファイルにあるのは関数である → ドットソースで読み込み
この場合は簡単で、**ドットソースで別ファイルを `profile.ps1` 等に展開** してあげればいい。

例)
```powershell
# C:/hoge.ps1
function hoge{
    Param([int]$num)
    for ($i = 0; $i -lt $num; $i++) {
        Write-Output "ほげ";
    }
}
```
```powershell
# profile.ps1
. C:/hoge.ps1 		# ドットソースで読み込み
```
実行
```
> hoge 2
ほげ
ほげ
```

加えてこの場合は以下のようにすると、manコマンド用にヘルプなどを書いて表示させることができる。  
(2024年8月6日 ファイルにミスがあったため修正)
```powershell
<#
.Description
与えられた回数分 hoge って言います
#>
function hoge{
    Param(
    	[int]$num
    )
    for ($i = 0; $i -lt $num; $i++) {
        Write-Output "ほげ";
    }
}
```
```ps1
> man hoge	# ちゃんとヘルプが表示される

名前
    hoge
概要

構文
    hoge [[-num] <Int32>] [<CommonParameters>]

説明
    与えられた回数分 hoge って言います
<省略>
```

詳しくは以下参照

[Operations Lab. "PowerShell スクリプトのコマンドヘルプの書き方（簡易版）"](https://operationslab.wordpress.com/2016/09/29/powershell-%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%98%E3%83%AB%E3%83%97%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9%EF%BC%88%E7%B0%A1%E6%98%93%E7%89%88/)

## 追記: まとめて別ファイルの関数を登録する方法
@AWtnb さんのこの記事に、まとめて自作コマンドを登録するわかりやすい方法が乗っていたので追記しておきます!


https://qiita.com/AWtnb/items/8a4a3c36378a621377ae


# case 02: 別ファイルにあるのはスクリプトである
## 解決策 01: alias でとりあえず別ファイルを実行する (簡単)

例
```powershell
# C:/hoge.ps1
Param([int]$num)

 
for ($i = 0; $i -lt $num; $i++) {
    Write-Output "ほげ";
}
```
```powershell
# profile.ps1
Set-Alias hoge C:/hoge.ps1
```
実行
```
> hoge 2
ほげ
ほげ
```

しかしこの場合はたとえファイルにヘルプがかかれていたとしても、 man コマンドで参照することはできない。

```powershell
# C:/hoge.ps1
<#
.Description
与えられた回数分 hoge って言います
#>

Param([int]$num)  
for ($i = 0; $i -lt $num; $i++) {
    Write-Output "ほげ";
}
```
```
> man hoge		# 思ったようにはならない

Name          : hoge
Category      : Alias
Synopsis      : hoge.ps1
Component     :
Role          :
Functionality :
```

ただし、この方法では `Executionpolicy` が `RemoteSigned` 以上でないと失敗する。 

## 解決策 02: `profle.ps1` に新たに関数を作り、別ファイルを実行する

1. profile.ps1 内に新たに関数 `hoge()` を作り、パラメタを渡す(ヘルプ用のコメントも書く)
2. そのパラメタをまた `hoge.ps1` に渡して実行する

という回りくどいことをすることになる。(どなたかもっとスマートなやり方があればご教示願います!)
```powershell
# C:/hoge.ps1
<#
.Description
与えられた回数分 hoge って言います
#>

Param([int]$num)  
for ($i = 0; $i -lt $num; $i++) {
    Write-Output "ほげ";
}
```
```powershell
# profile.ps1
function hoge {
    <#
    .Description
    与えられた回数分 hoge って言います
    #>

    Param([int]$num)  # hoge関数がパラメタを受け取り
    C:/hoge.ps1 $num 	# ps1ファイルに渡す
}
```

```
> hoge 2
ほげ
ほげ

> man hoge	# ちゃんと出る

名前
    hoge

概要

構文
    hoge [[-num] <Int32>] [<CommonParameters>]

説明
    与えられた回数分 hoge って言います
```

この方法も `Executionpolicy` が `RemoteSigned` 以上でないと失敗する。


## (追記:)解決策 03: 自作コマンド専用のディレクトリを作り、そこに Path を通す。

**恥ずかしながらこの方法をすっかり忘れていたので追記します。**   
まんま Linux と同じような方法である。
1. 自作コマンド専用のディレクトリを作る
1. そこに先ほどのps1ファイルを持って来る
1. ディレクトリにPathを通す

例)
1. `D:\mycmd\` ディレクトリを作る
1. そこに先ほどのファイルを持って来る
    ```powershell
    # D:\mycmd\hoge.ps1
    <#
    .Description
    与えられた回数分 hoge って言います
    #>
    
    Param([int]$num)  
    for ($i = 0; $i -lt $num; $i++) {
        Write-Output "ほげ";
    }
    ```
1. `D:\mycmd` にパスを通す(GUIでやるのが一番楽だが、Powershell からでもできる)

4. 実行
    ```sh
    > hoge 2  # hoge.ps1 2でも同じこと
    ほげ
    ほげ
    ```
5. man コマンド
    ```powershell
    > man hoge.ps1
    名前
        D:\mycmd\hoge.ps1
    概要
    
    構文
        D:\mycmd\hoge.ps1 [[-num] <Int32>] [<CommonParameters>]
    
    説明
        与えられた回数分 hoge って言います
    [省略]
    ```
ただしこの方法も `Executionpolicy` が `RemoteSigned` 以上でないと失敗する。

# 結論
**別ファイルに自作コマンドを書くときは関数にしよう!!!**  
**それか**(実行時ポリシーを変更して)**自作コマンド専用のディレクトリを作ろう**


# その他参考文献


https://stackoverflow.com/questions/62028630/why-wont-my-alias-give-me-get-help-or



---
2024年8月6日追記:  
解決策03・一部ファイルのミス・実行時ポリシーについて追記