Skip to content

Instantly share code, notes, and snippets.

@siniradam
Created December 10, 2025 22:26
Show Gist options
  • Select an option

  • Save siniradam/10ac6fd0d8829e8adcd8680baca9ae00 to your computer and use it in GitHub Desktop.

Select an option

Save siniradam/10ac6fd0d8829e8adcd8680baca9ae00 to your computer and use it in GitHub Desktop.
SSH Bookmark manager for quick connecting to ssh servers.
#!/bin/bash
# SSH Bookmarks Script - https://github.com/siniradam
# This script contains functions to store, list and quick connecting to frequently used SSH servers.
#
# I wrote this script to be used for mac, for other OS it might need some adjustment.
#
# `sshAdd <server_name> <server_ip> <username>` to add a record.
# `sshList` to list servers.
# `sshCon <server_name>` to connect.
# You can place this file in: ${HOME}/bin/ folder and source it in your .zprofile as: `source ~/bin/ssh-bookmarks.sh`
__ssh_store_path(){
printf '%s\n' "${HOME}/.config/secure-connect"
}
__ssh_store_file(){
printf '%s\n' "$(__ssh_store_path)/servers"
}
sshAdd(){
[[ $# -eq 3 ]] || { printf 'Usage: %s <name> <host> <user>\n' "${FUNCNAME[0]:-${0##*/}}"; return 1; }
local name="$1" host="$2" username="$3"
[[ -n "$name" && -n "$host" && -n "$username" ]] || { printf 'All parameters must be non-empty.\n' >&2; return 1; }
local store_dir="$(__ssh_store_path)"
local store_file="$(__ssh_store_file)"
[[ ! -e "$store_dir" || -d "$store_dir" ]] || { printf '%s exists and is not a directory.\n' "$store_dir" >&2; return 1; }
mkdir -p "$store_dir" || { printf 'Unable to create %s\n' "$store_dir" >&2; return 1; }
chmod 700 "$store_dir" 2>/dev/null || { printf 'Unable to set permissions on %s\n' "$store_dir" >&2; return 1; }
local tmp
tmp="$(mktemp "$store_dir/servers.XXXXXX")" || { printf 'Unable to create temporary file.\n' >&2; return 1; }
if [[ -s "$store_file" ]]; then
awk -F'|' -v n="$name" '$1 != n' "$store_file" > "$tmp" || { printf 'Failed to read existing entries.\n' >&2; rm -f "$tmp"; return 1; }
else
: > "$tmp" || { printf 'Failed to create temporary file.\n' >&2; rm -f "$tmp"; return 1; }
fi
printf '%s|%s|%s\n' "$name" "$host" "$username" >> "$tmp" || { printf 'Failed to write entry.\n' >&2; rm -f "$tmp"; return 1; }
mv "$tmp" "$store_file" || { printf 'Failed to update store.\n' >&2; rm -f "$tmp"; return 1; }
chmod 600 "$store_file" 2>/dev/null
printf '\033[33m'
printf 'Stored entry %s (%s@%s)\n' "$name" "$username" "$host"
printf '\033[0m'
return 0
}
sshList(){
local file
file="$(__ssh_store_file)"
[[ -s "$file" ]] || { printf 'No stored entries.\n'; return 0; }
# Make titles stand out with a different color
printf '\033[43m'
printf '%-20s %-40s %s\n' "NAME" "HOST" "USER"
printf '\033[0m'
while IFS='|' read -r n h u; do
[[ -z "$n" ]] && continue
printf '%-20s %-40s %s\n' "$n" "$h" "$u"
done < "$file"
}
sshCon(){
[[ $# -ge 1 ]] || { printf 'Usage: %s <name> [ssh_args...]\n' "${FUNCNAME[0]}"; return 1; }
local name="$1" file entry host user
shift
file="$(__ssh_store_file)"
[[ -r "$file" ]] || { printf 'Connection store unavailable.\n' >&2; return 1; }
entry="$(awk -F'|' -v n="$name" '$1 == n{print; exit}' "$file")"
[[ -n "$entry" ]] || { printf 'Entry %s not found.\n' "$name" >&2; return 1; }
IFS='|' read -r _ host user <<< "$entry"
[[ -n "$host" && -n "$user" ]] || { printf 'Corrupted entry for %s.\n' "$name" >&2; return 1; }
ssh "$user@$host" "$@"
}
sshUpdate(){
[[ $# -eq 3 ]] || { printf 'Usage: %s <name> <host> <user>\n' "${FUNCNAME[0]}"; return 1; }
local name="$1" host="$2" user="$3" file
file="$(__ssh_store_file)"
[[ -f "$file" ]] || { printf 'Entry %s not found.\n' "$name" >&2; return 1; }
if ! awk -F'|' -v n="$name" '$1 == n{found=1} END{exit !found}' "$file"; then
printf 'Entry %s not found.\n' "$name" >&2
return 1
fi
sshAdd "$name" "$host" "$user"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment