PowerShellでsshコマンドにssh_configの内容で入力補完できるようにする

本記事では`PowerShell`で`ssh`コマンド入力時に`~/.ssh/config`から`Host`項目を取得して入力補完する設定方法について説明します。

なお、`Windows`環境に`OpenSSH`をインストールする方法については下記を参照して下さい。

最近の`Windows`環境だと簡単にインストールできるようになっていますね。

[MS Docs/Install OpenSSH](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse)

## ドキュメント

`PowerShell`で入力補完機能を提供する`Register-ArgumentCompleter`コマンドレットのドキュメント

- [MS Docs/Register-ArgumentCompleter](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/register-argumentcompleter)

`ssh config`のマニュアル

[ssh config](https://man.openbsd.org/ssh_config)

## そもそもssh configって？

[ssh config](https://man.openbsd.org/ssh_config)のマニュアルに記載がありますが。

`ssh`では下記のように`コマンドラインオプション`や`設定ファイル`から構成を指定して`ssh`接続を実施します。

> 1.command-line options
> 2.user's configuration file (~/.ssh/config)
> 3.system-wide configuration file (/etc/ssh/ssh_config)

`ssh config`に構成を保存しておけば、`Host`指定で`ssh`接続できるようになり。毎回長いコマンドを打つ必要がなくなり便利です。　

```ssh
Host HelloWorld
 Hostname 192.0.2.10
 User ec2-user
 IdentityFile c:/Users/keita.saito/.ssh/private
```

上記のような設定が`config`ファイルにされている場合、`ssh Helloworld` と入力する事により、設定されているホスト名とユーザ名と秘密鍵で`ssh`接続する事ができます。

ここではこの`Helloworld`に相当する`Host`を`ssh config`から取得して`PowerShell`上に入力補完できるように表示させてみます。

## PowerShellで入力補完するには

`PowerShell`では[Register-ArgumentCompleter](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/register-argumentcompleter)というコマンドレットが用意されておりこちらを利用して、`ssh`コマンドに入力補完を追加する事ができます。

## PowerShellでsshに入力補完を追加する（簡易）

長々と説明してきましたが、下記をコピペして`PowerShell`に貼り付けて実行すると`ssh`で入力補完がつかえるようになります。

※注意　この方法では該当コマンドを実行したプロセス中のみ入力補完が有効となるため、`PowerShell`で毎回、`ssh`の入力補完を使いたい場合は`PowerShell`の起動時に読み込まれる`プロファイル`に該当のコマンドを追加して下さい。
なお`PowerShell`のプロファイルの場所については`PowerShell`コンソールで`$profile`と実行するとパスが取得できます。

`ssh`の後ろにスペースを入れた状態で`<TAB>`キー　もしくは`<CTRL+SPACE>`を入力すると入力補完が出てきます。

※`<CTRL+SPACE>`で入力補完の一覧を表示したイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b35c4757-e5d6-839d-5b07-6f7df847afc8.png)

```PowerShell:入力補完
Register-ArgumentCompleter -CommandName ssh, scp -Native -ScriptBlock {
  param($wordToComplete, $commandAst, $cursorPosition)

  # .ssh\config　からHost項目の一覧を取得
  $sshConfigHostList = (Get-Content ~\.ssh\config).trim() -replace "\s+", " " | Select-String -Pattern "^Host\s" | ForEach-Object { $_ -split "\s+" | Select-Object -Skip 1 }

  # Host一覧 から　入力値（$wordToComplete）に合致する物を補完対象。
  # [System.Management.Automation.CompletionResult]を生成して返す
  $sshConfigHostList | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
    $resultType = [System.Management.Automation.CompletionResultType]::ParameterValue
    # CompletionResult Class
    # completionText , listItemText , resultType toolTip
    [System.Management.Automation.CompletionResult]::new($_, $_, $resultType , $_)
  }
}
```

## 入力補完のツールチップに`ユーザ名@ホスト名`を表示してみる

今度の例ではツールチップに`ユーザ名@ホスト名`形式で表示をしてみます。

※画面赤枠のように`User`と`HostName`項目が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/233ed623-4a97-02dd-3219-6d7e7c3bece2.png)

※注意　`ssh config`のドキュメントをみるとわかりますが、`ssh config`ではワイルドカードマッチについて記載があります。
これは`Host`項目をワイルドカードで指定する事により、一箇所のパラメータを複数箇所に反映するような指定方法で。
今回の実装では、この記法に対応できていません。

`ssh config`をまじめにパースすると大変そうなので、このワイルドカードマッチを利用した`ssh config`の記法についてユーザ名とホスト名の表示は本スクリプトでは非対応となります。

```PowerShell:入力補完（ツールチップ対応）
# sshコマンド入力補完
Register-ArgumentCompleter -CommandName ssh, scp -Native -ScriptBlock {
  param($wordToComplete, $commandAst, $cursorPosition)

  # ~\.ssh\config取得(trimして空行と連続スペースは調整)
  $sshConfig = (Get-Content ~\.ssh\config).Trim() -replace "\s+", " " | Where-Object { $_ -ne "" }
  # Hostのグルーピング
  $sshConfigHostGroup = $sshConfig | Select-String -Pattern "^Host\s" -Context 0, $sshConfig.count | Select-Object Line , @{name = 'DisplayPostContext'; Expression = { $_.Context.DisplayPostContext } }

  # 入力補完　格納配列
  $autoCompleteList = New-Object System.Collections.ArrayList

  # toolTip用にHost項目に紐づくHostnameとUserを取得して配列作成
  foreach ($sshConfigHost in $sshConfigHostGroup) {
    # User取得（Hostの位置以降から直近のuser項目取得）
    $user = $sshConfigHost.DisplayPostContext | Select-String -Pattern "^User\s" | Select-Object -First 1 | ForEach-Object { $_ -split "\s" | Select-Object -Skip 1 -First 1 }
    # Hostname取得（Hostの位置以降から直近のHostname項目取得）
    $hostname = $sshConfigHost.DisplayPostContext | Select-String -Pattern "^Hostname\s" | Select-Object -First 1 | ForEach-Object { $_ -split "\s" | Select-Object -Skip 1 -First 1 }
    # Host単位で入力補完生成
    # Host hostA hostB
    $sshConfigHost.line -split "\s+" | Select-Object -Skip 1 | ForEach-Object {
      $autoCompleteList += [pscustomobject]@{Host = $_ ; toolTips = "$user@$hostname" }
    }
  }

  # [System.Management.Automation.CompletionResult]を生成して返す
  $autoCompleteList | Where-Object { $_.host -like "$wordToComplete*" } | ForEach-Object {
    $resultType = [System.Management.Automation.CompletionResultType]::ParameterValue
    # CompletionResult Class
    # completionText , listItemText , resultType toolTip
    [System.Management.Automation.CompletionResult]::new($_.host, $_.host, $resultType , $_.toolTips)
  }
}

```


## 総評

`PowerShell`では`Register-ArgumentCompleter`を利用すれば手軽に入力補完を実装できて便利です。

`ssh config`のドキュメントを読みましたが、`Host`に複数していする場合は`スペース`の指定ができたり。
`ワイルドカードマッチ`で複数箇所の設定が一箇所でできたり。

`ssh config`ファイルをちゃんとパースしようとすると、結構色々とありそうな気配があります。