feat(ssh): Load hosts in other ssh config files

If the ~/.ssh/config contains `Include` directives, it'd be better
to parse the target config files if possible. This commit implements that.
The included config can also have Include directives, but it's not parsed
because of possibility of infinite loop, for now.
This commit is contained in:
Roeniss Moon 2025-03-22 00:55:11 +09:00
parent 899af6328b
commit 1152d283fe
No known key found for this signature in database
GPG Key ID: 9E9AE088DBD537FB
2 changed files with 46 additions and 16 deletions

View File

@ -1,7 +1,8 @@
# ssh plugin
This plugin provides host completion based off of your `~/.ssh/config` file, and adds
some utility functions to work with SSH keys.
This plugin provides host completion based off of your `~/.ssh/config` file,
and adds some utility functions to work with SSH keys. If the `~/.ssh/config`
contains `Include` directives, the plugin also parse hosts in related files.
To use it, add `ssh` to the plugins array in your zshrc file:

View File

@ -2,20 +2,49 @@
# Take all host sections in .ssh/config and offer them for
# completion as hosts (e.g. for ssh, rsync, scp and the like)
# Filter out wildcard host sections.
_ssh_configfile="$HOME/.ssh/config"
if [[ -f "$_ssh_configfile" ]]; then
_ssh_hosts=($(
egrep '^Host.*' "$_ssh_configfile" |\
awk '{for (i=2; i<=NF; i++) print $i}' |\
sort |\
uniq |\
grep -v '^*' |\
sed -e 's/\.*\*$//'
))
zstyle ':completion:*:hosts' hosts $_ssh_hosts
unset _ssh_hosts
fi
unset _ssh_configfile
# If the .ssh/config has Include directives, load them too.
function _load_ssh_hosts {
local conf="$1"
if [[ -f "$conf" ]]; then
local _ssh_hosts=($(
egrep '^Host\ .*' "$conf" |\
awk '{for (i=2; i<=NF; i++) print $i}' |\
sort |\
uniq |\
grep -v '^*' |\
sed -e 's/\.*\*$//'
))
echo "${_ssh_hosts[@]}"
fi
}
# XXX: We could make it recursive but won't implement for now
function _find_include_files {
local conf="$1"
if [[ -f "$conf" ]]; then
egrep '^Include\ .*' "$conf" |\
awk '{for (i=2; i<=NF; i++) print $i}'
fi
}
all_hosts=($(_load_ssh_hosts "$HOME/.ssh/config"))
_find_include_files "$HOME/.ssh/config" | while read include_file; do
# omz doesn't know "~" directory
if [[ "$include_file" == ~* ]]; then
include_file="${HOME}${include_file:1}"
fi
if [[ -f "$include_file" ]]; then
hosts=($(_load_ssh_hosts "$include_file"))
all_hosts+=("${hosts[@]}")
fi
done
zstyle ':completion:*:hosts' hosts "${all_hosts[@]}"
unset -f _load_ssh_hosts
unset -f _find_include_files
unset all_hosts
############################################################
# Remove host key from known hosts based on a host section