SSH 接続先が増えてくると、~/.ssh/config がだんだん育ってきますよね。
自分の環境でも、例えばこんな感じで管理しています。
Host stg01.example.com
Hostname 123.456.789.000
Port 22
User ec2-user
IdentityFile ~/ssh/ssh_key/stg01.test.example.com.pem
Host web01.example.com
Hostname 111.111.111.111
Port 22
User ec2-user
IdentityFile ~/ssh/ssh_key/web01.example.com.pem
普段は web01.example.com のように Host 名で接続できるのでかなり便利です。
ただ、ログや監視通知、踏み台の接続記録なんかで IP アドレスだけを見たときに、
111.111.111.111 ってどの Host だっけ?
となることがちょこちょこありました。
そのたびに ~/.ssh/config を開いて検索するのは、できなくはないけど地味に面倒です。
そこで、IP アドレスを渡すと対応する Host 名を返してくれる sship という小さなスクリプトを作ってみました。
こんなことをしたい
やりたいことはシンプルで、こんな感じです。
sship 123.456.789.000
# => stg01.example.com
同じ IP に複数の Host 名がぶら下がっている場合は、まとめて全部出してほしいです。
sship 222.222.222.222
# => slave.stg01.example.com
# => slave.stg01.example.com_homepage
# => slave.stg01.example.com_planning_ssh
作成したスクリプト
中身は zsh + awk のシンプルなスクリプトです。
#!/bin/zsh
set -eu
config_file="${SSH_CONFIG:-$HOME/.ssh/config}"
if [ $# -ne 1 ]; then
echo "Usage: $(basename "$0") <ip-or-hostname>" >&2
exit 1
fi
target="$1"
awk -v target="$target" '
BEGIN {
count = 0
}
/^[[:space:]]*#/ {
next
}
/^[[:space:]]*Host[[:space:]]+/ {
line = $0
sub(/^[[:space:]]*Host[[:space:]]+/, "", line)
split(line, parts, /[[:space:]]+/)
delete current_hosts
current_host_count = 0
for (i in parts) {
host = parts[i]
if (host == "" || host ~ /[*?]/) {
continue
}
current_hosts[++current_host_count] = host
}
next
}
/^[[:space:]]*Hostname[[:space:]]+/ {
line = $0
sub(/^[[:space:]]*Hostname[[:space:]]+/, "", line)
split(line, parts, /[[:space:]]+/)
hostname_value = parts[1]
if (hostname_value != target) {
next
}
for (i = 1; i <= current_host_count; i++) {
host = current_hosts[i]
if (!(host in seen)) {
print host
seen[host] = 1
count++
}
}
}
END {
if (count == 0) {
exit 2
}
}
' "$config_file" || {
exit_code=$?
if [ "$exit_code" -eq 2 ]; then
echo "No Host found for: $target" >&2
fi
exit "$exit_code"
}
仕組みは素朴
やっていることはかなり単純で、~/.ssh/config を上から順番に読みながら、
直前の Host と、その後に出てくる Hostname を対応付けているだけです。
流れとしてはこうなります。
-
Host ...の行を見つけたら、そのブロックの Host 名を一時的に覚える -
Hostname ...の行を見つけたら、入力された IP アドレスと一致するか比較する - 一致したら、さっき覚えていた Host 名を表示する
- 同じ Host 名が重複して出ないように制御する
awk だけで完結しているので、依存が少なくて雑に置いても動かしやすいです。
使い方
先ほどのスクリプトをDownloads配下に置いた場合は以下のようにして実行します。
@iMac-PC Downloads % ./ssh-host-by-ip 111.111.111.111
web01.example.com
ただ毎度./ssh-host-by-ipを入力も面倒なので自分は~/.zshrcにaliasを設定してsshipで動作するようにしてます。
#aliasの設定
echo "alias sship='~/Downloads/ssh-host-by-ip'" >> ~/.zshrc
source ~/.zshrc
#動作検証
@iMac-PC Downloads % sship 111.111.111.111
web01.example.com
sship {IPアドレス}
だけで検索できるようになりました!
こっちのが利便性高いですね
※Downloadsにスクリプト本体を置くのはあまり良くないので、実際は別のところに置くことをお勧めいたします。
補足
このスクリプトは Host と Hostname のシンプルな対応をたどる作りです。
そのため、Include や複雑な Match 条件まで厳密に解釈するものではありません。
とはいえ、よくある「~/.ssh/config に接続先をずらっと並べている」運用なら、かなり実用的だと思います。
まとめ
IP アドレスだけ分かっているときに、~/.ssh/config から対応する Host 名をすぐ引けるようになったので、
ちょっとした調査や接続確認がだいぶ楽になりました。
派手な話ではないですが、こういう小さい道具は日々のストレスをちゃんと減らしてくれるので結構おすすめです。
~/.ssh/config をよく使う人なら、1つ置いておくとわりと便利だと思います。