PowerShellでのsshコマンドのタブ補完設定

## はじめに
私は普段Windowsを使用しているのですが、PowerShellを使ってサーバにsshした際「あれ？Tabを押しても接続先の候補が補完されないぞ？　Linuxでは補完されたよな？」となりました。  

PowerShellでsshした際に接続先候補が補完できるようにしたので、設定方法についてまとめていきたいと思います。  


## やったこと
やったことをまとめると以下になります。  
1. `.ssh\config`の作成
2. スクリプトブロックに、`.ssh\config`からHost情報だけを抽出するスクリプトを記述
3. `Register-ArgumentCompleter`を使用してタブ補完を登録
4. タブ補完設定の永続化

## `.ssh\config`の作成 
例として、以下のような`.ssh\config`を使用します。
```.ssh\config
Host SERVER_1
    HostName EXAMPLE1.JP
    User hoge1
    Port 22

Host SERVER_2
    HostName EXAMPLE2.JP
    User hoge2
    Port 22
```

## Host情報を抽出するスクリプト作成
PowerShellプログラミング言語では、スクリプトをスクリプトブロックという１つのまとまりとして定義できるそうです。  
詳細は[公式の説明](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.4)を参照してください。  

作成したスクリプトブロックは以下になります。  
`<your\ssh\config\path>`の部分を、読み取りたい`.ssh\config`のパスに書き換えてください。
``` 
$scriptBlock = {
    cat <your\ssh\config\path> | Where-Object {$_ -match "^host "} | ForEach-Object {$_.Remove(0, 5)}
    }
```

### 説明
スクリプトブロックでは、３つのコマンドをパイプでつなげて接続先の名称のみを抽出しています。  

`cat <your\ssh\config\path>`  
configに記述されている内容を取得しています。  

`Where-Object {$_ -match "^host "}`  
前処理で各行の記述内容がオブジェクトとして得られるため、正規表現の前方一致で "Host" から始まっている行のみを選択します。  

`ForEach-Object {$_.Remove(0, 5)}`  
前処理から得られた行に対して、先頭から5文字目まで("host" + 空白)を削除することで接続先名称のみを抽出します。

## タブ補完の登録
PowerShellを開き、下記のように実行します。
``` 
PS> $scriptBlock = {
    cat <your\absolute\ssh\config\path> | Where-Object {$_ -match "^Host "} | ForEach-Object {$_.Remove(0, 5)}
    }

PS> Register-ArgumentCompleter -CommandName ssh -ScriptBlock $scriptBlock
```

### 説明
`Register-ArgumentCompleter`を使用して、sshコマンドのタブ補完を登録しています。  
タブ補完で表示される候補は`$scriptBlock`より得られた値です。

上記コマンドを実行した場合、実行したPowerShellでしかタブ補完が使えません。  
また、PowerShellを閉じるとタブ補完は使えなくなります。  
次の手順ではタブ補完を永続化させる設定を実施します。

## タブ補完設定の永続化
PowerShellプロファイルに先ほどのスクリプトを設定することで、タブ補完設定を永続化させます。それではタブ補完を永続化させる方法について説明していきます。  

詳しくは[公式の説明](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.4)を見ていただきたいのですが、PowerShellプロファイルの種類と保存場所にはいくつか種類があるようです。  
今回は、「現在のユーザー、現在のホスト」のプロファイルを使用したいと思います。

```Microsoft.PowerShell_profile.ps1
$scriptBlock = {
    cat <your\absolute\ssh\config\path> | Where-Object {$_ -match "^Host "} | ForEach-Object {$_.Remove(0, 5)}
    }

Register-ArgumentCompleter -CommandName ssh -ScriptBlock $scriptBlock
```

## おわりに
以上、PowerShellでsshした際のタブ補完設定についてまとめてみました。  
sshコマンド以外にも応用できるので、タブ補完が使えないなと思った際には活用していきたいと思います。

## 参考サイト
- [スクリプトブロックについて](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.4)
- [Register-ArgumentCompleter](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/register-argumentcompleter?view=powershell-7.4)
- [プロファイルについて](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.4)
- [Where-Object](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/where-object?view=powershell-7.4)
- [ForEach-Object](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.4)