shell-utils/ssh.sh
2026-06-03 09:53:36 +08:00

134 lines
3.6 KiB
Bash
Executable File

#!/bin/sh
set -eu
KNOWN_CRED_FILE="$HOME/.ssh/known_creds"
KNOWN_HOSTS_FILE="$HOME/.ssh/known_hosts"
# === 1. Load credentials from file ===
TMP_CREDS=$(mktemp)
trap 'rm -f "$TMP_CREDS"' EXIT
if [ -f "$KNOWN_CRED_FILE" ]; then
cp "$KNOWN_CRED_FILE" "$TMP_CREDS"
fi
# === 2. Parse known_hosts (deduped) ===
TMP_HOSTS=$(mktemp)
trap 'rm -f "$TMP_CREDS" "$TMP_HOSTS"' EXIT
if [ -f "$KNOWN_HOSTS_FILE" ]; then
grep -v '^[[:space:]]*#' "$KNOWN_HOSTS_FILE" | \
awk '
{
split($1, parts, ",")
for (i in parts) {
h = parts[i]
gsub(/[][]/, "", h)
port = 22
if (match(h, /:[0-9]+$/)) {
port = substr(h, RSTART+1)
h = substr(h, 1, RSTART-1)
}
key = h ":" port
if (!(key in seen)) { seen[key] = 1; print h "|" port }
}
}' > "$TMP_HOSTS"
fi
# Check if hosts found
if [ ! -s "$TMP_HOSTS" ]; then
echo "⚠️ No hosts in ~/.ssh/known_hosts. Run 'ssh user@host' first." >&2
exit 1
fi
# === 3. Build menu + store full credentials ===
hosts_data=()
echo "📋 Available hosts:"
i=1
while IFS='|' read -r host port; do
user=""
pass=""
# Safely extract credentials (avoid grep failure)
match=$(grep "^$host|" "$TMP_CREDS" 2>/dev/null | head -n1) || true
if [ -n "$match" ]; then
IFS='|' read -r _ user _ pass <<< "$match"
fi
# Determine mode: 1 = auto-pass, 0 = manual
mode=0
if [ -n "$user" ] && [ -n "$pass" ]; then
mode=1
fi
hosts_data+=("$host|$port|$user|$pass|$mode")
if [ "$mode" -eq 1 ]; then
echo "$i) $host ($port) — $user (auto-pass)"
else
echo "$i) $host ($port)"
fi
i=$((i + 1))
done < "$TMP_HOSTS"
echo "$i) Cancel"
# === 4. Prompt for host ===
PS3="Select: "
while true; do
read -r choice || exit 0
if [ "$choice" = "$i" ]; then
echo "Cancelled."
exit 0
elif [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
break
fi
echo "Invalid. Try again:"
done
# Extract selected host/credentials
item="${hosts_data[$((choice-1))]}"
IFS='|' read -r host port user pass mode <<< "$item"
# === 5. Get credentials if not auto-pass ===
if [ "$mode" -eq 0 ]; then
read -rp "Username [$USER]: " user
user="${user:-$USER}"
stty -echo 2>/dev/null || true
read -rp "Password: " pass
stty echo 2>/dev/null || true
echo
# Save if requested
read -rp "Save credentials? [y/N]: " save
if [ "$save" = "y" ] || [ "$save" = "Y" ]; then
if [ -f "$KNOWN_CRED_FILE" ]; then
grep -v "^$host|" "$KNOWN_CRED_FILE" > "${KNOWN_CRED_FILE}.tmp" || true
mv "${KNOWN_CRED_FILE}.tmp" "$KNOWN_CRED_FILE"
fi
echo "$host|$user|$port|$pass" >> "$KNOWN_CRED_FILE"
chmod 600 "$KNOWN_CRED_FILE"
fi
fi
# === 6. Launch SSH — reuse existing window/pane ===
# Ensure sshpass is available
if ! command -v sshpass >/dev/null 2>&1; then
echo "⚠️ Install sshpass: brew install hudochenkov/sshpass/sshpass"
exit 1
fi
# Get WezTerm pane ID if available
pane_id=""
if osascript -e 'application "WezTerm" is running' >/dev/null 2>&1; then
pane_id=$(wezterm cli list-panes -q 2>/dev/null | head -n1 | cut -d' ' -f1) || true
fi
# Create safe command with quoted password
safe_cmd=$(printf 'sshpass -p %q ssh -p %s %s@%s' "$pass" "$port" "$user" "$host")
# Reuse pane or spawn new tab
if [ -n "$pane_id" ]; then
wezterm cli send-text --pane "$pane_id" "$safe_cmd\n"
else
# Fallback: spawn new tab in existing window
wezterm cli spawn --cwd "$HOME" -- sh -c "$safe_cmd"
fi