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" ]] \
|| 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
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line#"${line%%[![:space:]]*}"}"
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
key="${line%% *}"
if [[ "$key" == "Host" ]]; then
# Force whitespace-aware splitting (the script's global IFS excludes spaces).
local IFS=$' \t\n'
while read -r line || [[ -n "$line" ]]; do
# First whitespace-separated token (tabs and spaces collapse uniformly).
read -r first _ <<< "$line"
[[ -z "$first" || "$first" == "#" ]] && continue
if [[ "$first" == "Host" ]]; then
if (( in_block )); then break; fi
read -r -a patterns <<< "${line#Host }"
read -r -a patterns <<< "$line"
patterns=("${patterns[@]:1}")
in_block=0
for p in "${patterns[@]}"; do
if [[ "$p" == "$alias" ]]; then
@@ -149,12 +153,13 @@ resolve_ssh_alias() {
done
continue
fi
if (( in_block )); then
case "$key" in
HostName) hostname="${line#* }" ;;
User) user="${line#* }" ;;
Port) port="${line#* }" ;;
IdentityFile) identity="${line#* }" ;;
case "$first" in
HostName) read -r _ hostname <<< "$line" ;;
User) read -r _ user <<< "$line" ;;
Port) read -r _ port <<< "$line" ;;
IdentityFile) read -r _ identity <<< "$line" ;;
esac
fi
done < "$config"