カテゴリー
プログラム 社内SE

sshのconfigのhostをリスト表示し、そのままssh

sshコマンドを打つときに.configに設定したホスト名を忘れて躓く方のための補助用のスクリプトです。
Windows Powershell版Linux bash版それぞれあります。個人用に作成しましたので細かいエラーはご容赦のほど。

使い方

コマンド:sshfc [ホスト名の一部]

sshfcとだけ打つとconfigファイルに設定した全ホスト名が表示されます。
sshfc sysのように第一引数をつけて打つとsysを含むホスト名が表示されます。続けて番号選択でそのままSSH接続です。
コマンド名は”ssh from config” の頭をとってsshfcとしています。以下使用例になります。

C:\> sshfc sys
1) system
2) test_system
3) dev_system
SSHするホストの番号を入力してください。[c]を押すとキャンセルされます。: 3
ssh dev_system
hogehoge@192.168.1.100's password:
....

Windows Powershell版

スクリプト

sshfc.ps1として以下のファイルを作成し、SJISかBOM付UTF-8で保存します。(普通のUTF-8はNGです。実行時に文字化けします。メモ帳の[名前を付けて保存]で UTF-8 BOM付きを選択)

using namespace Microsoft.VisualBasic

# paramater
Param($targetHost)
if(-not ([string]::IsNullOrEmpty($targetHost))){
	$targetHost = $targetHost.trim().ToLower()
}else{
	$targetHost = ""
}

# local
$jap = $FALSE
$local = Get-WinSystemLocale
if ($local.Name.Contains('ja')){
	$jap = $TRUE
}

# config file path
$configFile = $HOME + "\.ssh\config"
if( $(test-path $configFile) -ne $True ){
	if ($jap){
		echo "設定ファイル(.ssh\config)がありません"
	}else{
		echo "the .ssh\config file doesn't exist..."
	}
	exit 1
}

# read the config file
$hostNames = @()
$lines = get-content $configFile
foreach($line in $lines){
	$command, $hostname = $line.trim() -split '\s+|\t+'
	# if ($command -eq "host" -Or $command -eq "hostname"){
	if ($command -eq "host"){
		if ($targetHost -eq ""){
			$hostNames += $hostname
		}else{
			if ($hostname.ToLower().Contains($targetHost)){
				$hostNames += $hostname
			}
		}
	}
}

$lenHostNames = $hostNames.Count
if ($lenHostNames -eq 0){
	if ($targetHost -eq ""){
		if ($jap){
			write-host("設定ファイルにホストが見つかりません。")
		}else{
			write-host("didn't find any hosts in the config file.")
		}
	}else{
		if ($jap){
			write-host("[" + $targetHost + "]を持つホストが見つかりません。")
		}else{
			write-host("didn't find any hosts including [" + $targetHost + "].")
		}
	}
	exit 1
}

for ( $index = 0; $index -lt $lenHostNames; $index++){
	write-host ( ([string]($index+1)) + ") " + $hostNames[$index] )
}

$strInput = ""
if ($jap){
	$strInput = "SSHするホストの番号を入力してください。[c]を押すとキャンセルされます。"
}else{
	$strInput = 'Press the number of the host to ssh. Press [c] if you want to cancel.'
}

Add-Type -AssemblyName "Microsoft.VisualBasic"

# loop until valid data comming or 10 times mistake
$bOKInput = $FALSE;
$input = ""
for ($index = 0; $index -lt 10; $index++){
	$input = Read-Host($strInput);
	$input = [Strings]::StrConv($input, [VbStrConv]::Narrow)
	if ($input.ToLower() -eq "c"){
		exit 1
	}
	if ($input -match "[0-9]+"){
		$input = [int]$input
		if ($input -le 0 -Or $input -gt $lenHostNames){
			if ($jap){
				$strInput = "認識できません。番号を再入力してください。"
			}else{
				$strInput = 'Failed to get the number. Press the number again. '
			}
			continue
		}else{
			$bOKInput = $TRUE
			break;
		}
	}
}
if ($bOKInput){
	write-host("ssh " + $hostNames[$input-1])
	ssh $hostNames[$input-1]
}

exit 0

スクリプトの設置

以下をPowershellで実行し、ユーザディレクトリにbinフォルダを作成します。

New-Item ($HOME + "\bin") -ItemType Directory

続いて以下のコマンドでメモ帳を起動

notepad $profile

メモ帳が起動し、Windows terminalの設定ファイルが開かれるのでメモ帳に以下を記載します。(初めて設定する場合はブランクのファイルが開きますので以下を記載。)

$env:path += ";" + $HOME + "\bin"
$OutputEncoding = [System.Text.Encoding]::GetEncoding('utf-8')

VSCODEなど他のソフトでも使う場合は、以下のコマンドでPATHを設定します。(こちらを使用する場合は上記のWindows terminalの設定は不要です。)

[System.Environment]::SetEnvironmentVariable("Path", $env:Path + ";%USERPROFILE%\bin", "User")

スクリプトファイル(sshfc.ps1)をユーザディレクトのbinフォルダにコピーします。フォルダの場所がわからない場合は、以下のコマンドで表示される場所がコピーする場所になります。

$HOME + "bin"

これでPowershellからsshfcコマンドが使えるようになるはずです。

注意事項

スクリプト自体が実行できない場合は以下コマンドを実行してみてください。スクリプトの実行制限を緩めます。

Set-ExecutionPolicy Unrestricted -scope CurrentUser

Linux Bash版

スクリプト

以下のファイルをsshfcとして作成します。Ubuntuで使っているのでShebangは#!/bin/bashにしています。動かない場合はそれぞれの環境のbashのパスにしてください。

#!/bin/bash

#paramater
targetName=""
if [ $# -gt 0 ]; then
    targetName=$1
fi

SSH_CONFIG=$HOME/.ssh/config
if [ ! -e $SSH_CONFIG ]; then
    echo "couldn't find the ssh config file. [$(SSH_CONFIG)]"
    exit 0
fi

aryHosts=()
while read line
do
    # trim
    echo $line > line
    # replace tabs to spaces
    echo ${line//\t/" "} > line
    # sumup spaces to a space
    echo "$line" | sed -e 's/  */ /g' > line
    # is this target line?
    if [ -n "$line" ] && [ "`echo $line | grep -i '^host '`" ]; then
        hostname="${line:5}"
        if [ -n "$targetName" ]; then
            if [ "`echo $hostname | grep -i $targetName`" ]; then
                aryHosts+=($hostname)
            fi
        else
            aryHosts+=($hostname)
        fi
    fi

done < $SSH_CONFIG

i=1
for hostname in "${aryHosts[@]}"
do
    echo "$i) $hostname"
    ((i++))
done

# 20 is a safty limit to avoid an infinity loop
for ((i=0; i < 20; i++)); do
    read -p 'Enter the number to ssh. C will be canceled. : ' selectedNum
    if [ $selectedNum = "C" ] || [ $selectedNum = "c" ]; then
        exit 0
    fi

    # number entered?
    if expr "$selectedNum" : "[0-9]*$" >&/dev/null;then
        if [ $selectedNum -gt 0 ] && [ $selectedNum -le ${#aryHosts[@]} ]; then
            #echo ${aryHosts[$(($selectedNum-1))]}
            echo "ssh ${aryHosts[$(($selectedNum-1))]}"
            ssh ${aryHosts[$(($selectedNum-1))]}
            break
        fi
    fi
done

exit 0

配置方法

rootで~/sshfcというファイルで作ったとして以下のコマンドで実行権限付与し、PATHの下へファイル移動します。

chmod 777 ~/sshfc
mv ~/sshfc /usr/local/bin

雑記

最近、WindowsのsshクライアントをMicrosoft謹製の「Windows terminal」に変更しました。 「Windows terminal」 自体は秀逸なのですが、以前使用していたsshクライアントと比べ若干使いずらいのが、SSHの接続先の選択方法。なのでツールを作ってカバーした感じです。

作ったツールが思いのほか便利だったので、bash版も作成しサーバに入った後のSSHも同様にできるようにしました。「.configに設定したホスト名なんだっけ?」と考えることが無くなりました。

Powershell のスクリプトを書くのは初めて。こういった作業効率化のちょっとしたコードを書く分には構文に慣れている分、過去のvbscriptで良いかなあという印象です。bashのスクリプトも簡単な数行ものを除けば、めったに書かないのでいつも構文忘れます。

Powershell版は日英つけていますがps1ファイルは配布が難しいので意味ないです。Bash版は面倒なので英語だけです。