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
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 # ssh plugin
This plugin provides host completion based off of your `~/.ssh/config` file, and adds This plugin provides host completion based off of your `~/.ssh/config` file,
some utility functions to work with SSH keys. 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: 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 # Take all host sections in .ssh/config and offer them for
# completion as hosts (e.g. for ssh, rsync, scp and the like) # completion as hosts (e.g. for ssh, rsync, scp and the like)
# Filter out wildcard host sections. # Filter out wildcard host sections.
_ssh_configfile="$HOME/.ssh/config" # If the .ssh/config has Include directives, load them too.
if [[ -f "$_ssh_configfile" ]]; then function _load_ssh_hosts {
_ssh_hosts=($( local conf="$1"
egrep '^Host.*' "$_ssh_configfile" |\ if [[ -f "$conf" ]]; then
awk '{for (i=2; i<=NF; i++) print $i}' |\ local _ssh_hosts=($(
sort |\ egrep '^Host\ .*' "$conf" |\
uniq |\ awk '{for (i=2; i<=NF; i++) print $i}' |\
grep -v '^*' |\ sort |\
sed -e 's/\.*\*$//' uniq |\
)) grep -v '^*' |\
zstyle ':completion:*:hosts' hosts $_ssh_hosts sed -e 's/\.*\*$//'
unset _ssh_hosts ))
fi echo "${_ssh_hosts[@]}"
unset _ssh_configfile 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 # Remove host key from known hosts based on a host section