fix: parse SSH config entries with tabs as well as spaces

resolve_ssh_alias() now extracts tokens with 'read -r', which
collapses tabs and spaces uniformly. Two bugs were at play:

1. The previous parser split on literal spaces
   (key="${line%% *}") and used '${line#Host }' to drop the
   Host directive, so configs that used tab indentation
   (e.g. '\tHostName 10.60.16.35') ended up with key being the
   whole line and never matching the case branches. Result:
   'has no HostName in ~/.ssh/config' even when one was present.

2. The script's global IFS is $'\n\t' (no space), which made
   'read' unable to split on spaces at all. Fixed by restoring
   space-aware IFS locally inside the loop.

Verified against tabbed, spaced, mixed, indented-comment and
Host\tname configs; behavior with the original space-separated
configs is unchanged.
This commit is contained in:
2026-09-09 11:19:13 -03:00
parent 38b22648f1
commit 4d55c8b8bb
+17 -12
View File
@@ -130,15 +130,19 @@ resolve_ssh_alias() {
[[ -f "$config" ]] \ [[ -f "$config" ]] \
|| die "Cannot resolve '$alias': $config not found and value has no '@'." || die "Cannot resolve '$alias': $config not found and value has no '@'."
local in_block=0 hostname="" user="" port="" identity="" line key value p local in_block=0 hostname="" user="" port="" identity="" line first p
local -a patterns local -a patterns
while IFS= read -r line || [[ -n "$line" ]]; do # Force whitespace-aware splitting (the script's global IFS excludes spaces).
line="${line#"${line%%[![:space:]]*}"}" local IFS=$' \t\n'
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue while read -r line || [[ -n "$line" ]]; do
key="${line%% *}" # First whitespace-separated token (tabs and spaces collapse uniformly).
if [[ "$key" == "Host" ]]; then read -r first _ <<< "$line"
[[ -z "$first" || "$first" == "#" ]] && continue
if [[ "$first" == "Host" ]]; then
if (( in_block )); then break; fi if (( in_block )); then break; fi
read -r -a patterns <<< "${line#Host }" read -r -a patterns <<< "$line"
patterns=("${patterns[@]:1}")
in_block=0 in_block=0
for p in "${patterns[@]}"; do for p in "${patterns[@]}"; do
if [[ "$p" == "$alias" ]]; then if [[ "$p" == "$alias" ]]; then
@@ -149,12 +153,13 @@ resolve_ssh_alias() {
done done
continue continue
fi fi
if (( in_block )); then if (( in_block )); then
case "$key" in case "$first" in
HostName) hostname="${line#* }" ;; HostName) read -r _ hostname <<< "$line" ;;
User) user="${line#* }" ;; User) read -r _ user <<< "$line" ;;
Port) port="${line#* }" ;; Port) read -r _ port <<< "$line" ;;
IdentityFile) identity="${line#* }" ;; IdentityFile) read -r _ identity <<< "$line" ;;
esac esac
fi fi
done < "$config" done < "$config"