Mac上用 iTerm2 打造轻量 SSH 工作流

适用人群:需要日常 SSH 到 Linux 服务器的 Mac 用户,希望用最小成本获得类似 Xshell 的体验

核心工具 :iTerm2 + ~/.ssh/config + Shell 函数(scp/rsync

不需要安装:任何第三方传输工具、sshfs、Electron App


目录

  1. [背景:为什么不用 Xshell / SecureCRT?](#背景:为什么不用 Xshell / SecureCRT?)
  2. 方案全景
  3. 环境准备
  4. [第一步:生成 SSH 密钥对](#第一步:生成 SSH 密钥对)
  5. [第二步:配置 ~/.ssh/config](#第二步:配置 ~/.ssh/config)
  6. 第三步:把公钥上传到服务器
  7. [第四步:配置 iTerm2 Profile](#第四步:配置 iTerm2 Profile)
  8. 第五步:配置文件传输函数
  9. 日常使用示例
  10. 多台服务器的管理技巧
  11. 常见问题排查
  12. 完整配置汇总

1. 背景

Xshell 是 Windows 上老牌的 SSH 客户端,功能全面,但它没有原生的 macOS 版本。Mac 用户通常用 SecureCRT、Termius、Royal TSX 等工具替代,这些工具都要付费或有功能限制。

其实 macOS 自带的工具链已经相当完整:

  • OpenSSH 客户端:系统自带,支持密钥认证、连接复用、Keep-alive 等
  • scp / rsync:文件传输,同样系统自带
  • ~/.ssh/config:SSH 的"通讯录",把服务器信息存进去,以后不用记 IP

配合 iTerm2 的 Profiles 功能,整套体验并不比 GUI 工具差,而且:

  • 零额外软件成本(iTerm2 本身免费)
  • 无需维护依赖,macOS 系统升级不影响
  • 纯文本配置,备份只需要复制几个文件

本文把这套工作流从头到尾完整走一遍。


2. 方案全景

完成配置后,整个工作流是这样的:

shell 复制代码
连接服务器:
  Cmd+O → 输入服务器名 → 回车 → 直接进入 Shell(无需输入密码)

上传文件:
  put ~/Downloads/config.json /etc/myapp/

下载文件:
  get /var/log/app.log ~/Desktop/

同步整个目录:
  sync-up ./dist /var/www/myapp

涉及的文件清单:

文件 作用
~/.ssh/config 保存服务器地址、用户名、密钥路径等,SSH/scp/rsync 共用
~/.ssh/id_ed25519 私钥(只存本机,不要泄露)
~/.ssh/id_ed25519.pub 公钥(上传到服务器的 ~/.ssh/authorized_keys
~/.zshrc Shell 配置,存放文件传输快捷函数

3. 环境准备

3.1 确认 OpenSSH 已安装

macOS 自带 OpenSSH,正常不需要额外安装,确认一下:

bash 复制代码
ssh -V

输出类似 OpenSSH_9.x, LibreSSL x.x.x 即为正常。

3.2 安装 Homebrew(如果还没装)

后续不需要用 Homebrew 安装任何东西,但如果你的 Mac 上还没有,建议顺手装上,以后会经常用到:

bash 复制代码
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

3.3 安装 iTerm2

前往 https://iterm2.com 下载,或用 Homebrew:

bash 复制代码
brew install --cask iterm2

安装完毕后打开,后续操作都在 iTerm2 里完成。


4. 第一步:生成 SSH 密钥对

SSH 密钥认证是比密码登录更安全、更方便的方式。生成一次,以后连接服务器无需每次输密码。

4.1 检查是否已有密钥

bash 复制代码
ls ~/.ssh/

如果看到 id_ed25519id_ed25519.pub(或 id_rsaid_rsa.pub),说明已经有了,可以跳过生成步骤,直接用现有密钥。

4.2 生成新密钥

推荐用更现代、更安全的 Ed25519 算法(比老式的 RSA 4096 更短、更快、同样安全):

bash 复制代码
ssh-keygen -t ed25519

执行后会有几个交互提示:

bash 复制代码
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/yourname/.ssh/id_ed25519):

直接回车,使用默认路径即可。

bash 复制代码
Enter passphrase (empty for no passphrase):

这里是给私钥设置一个"加密密码"。有两种选择:

  • 留空(直接回车):私钥不加密,连接服务器完全不用输任何密码,最方便,但私钥文件如果泄露有风险
  • 设置 passphrase:每次使用私钥时需要输入这个密码(可以配合 macOS Keychain 缓存,只需第一次输入)

个人服务器日常使用,留空即可。如果是工作环境或对安全要求高,建议设置 passphrase 并结合 ssh-agent 使用。

生成完毕后:

bash 复制代码
ls ~/.ssh/
# 应该看到:
# id_ed25519      ← 私钥,绝对不要传给任何人
# id_ed25519.pub  ← 公钥,这个需要上传到服务器

4.3 设置密钥文件权限

SSH 对私钥文件权限有严格要求,权限过宽会拒绝使用:

bash 复制代码
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

5. 第二步:配置 ~/.ssh/config

这是整个方案的核心文件,相当于 SSH 的"通讯录"。配置好后,sshscprsync 都会自动读取这里的信息,不用每次手动输入 IP、用户名、端口。

5.1 创建或编辑配置文件

bash 复制代码
touch ~/.ssh/config
chmod 600 ~/.ssh/config    # 权限必须是 600,否则 SSH 会忽略此文件
open -e ~/.ssh/config      # 用文本编辑器打开(或用 vim/nano 也行)

5.2 配置文件语法说明

一个基本的 Host 块如下:

bash 复制代码
Host <别名>
    HostName <IP 或域名>
    User <登录用户名>
    Port <SSH 端口,默认 22>
    IdentityFile <私钥路径>
  • Host 后面的是别名 ,自己起,用于连接时的简称(ssh 别名
  • 每一行的配置项前面有 4 个空格缩进(或一个 Tab),这是规范写法
  • # 开头的行是注释

5.3 完整配置示例

bash 复制代码
# ========== 服务器 1:个人云服务器 ==========
Host myserver
    HostName 1.2.3.4
    User root
    Port 22
    IdentityFile ~/.ssh/id_ed25519

# ========== 服务器 2:公司开发机 ==========
Host dev
    HostName dev.mycompany.com
    User ubuntu
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

# ========== 全局设置(对所有连接生效)==========
Host *
    # 每 60 秒发一次心跳包,防止长时间空闲被服务器踢掉
    ServerAliveInterval 60
    # 心跳失败 3 次才真正断开
    ServerAliveCountMax 3
    # 开启连接复用:同一台服务器的多个 SSH 连接共用一条 TCP 连接
    # 好处:再次连接同一台服务器时几乎瞬间建立(跳过握手阶段)
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 10m

配置好后验证一下:

bash 复制代码
ssh -T myserver
# 如果能连上并出现 Shell,说明配置正确

6. 第三步:把公钥上传到服务器

只需做一次。把本机的公钥添加到服务器的 authorized_keys 文件里,以后就能免密码登录。

6.1 方法一:使用 ssh-copy-id(推荐,最简单)

bash 复制代码
ssh-copy-id -i ~/.ssh/id_ed25519.pub myserver

这条命令会:

  1. 用密码登录服务器(最后一次需要输密码)
  2. 自动把公钥追加到服务器的 ~/.ssh/authorized_keys
  3. 自动设置好相关权限

完成后立刻测试:

bash 复制代码
ssh myserver
# 应该不用输密码就能进入 Shell

6.2 方法二:手动上传(如果 ssh-copy-id 不可用)

先把公钥内容复制到剪贴板:

bash 复制代码
cat ~/.ssh/id_ed25519.pub | pbcopy

然后用密码 SSH 登录到服务器,手动添加:

bash 复制代码
ssh root@1.2.3.4  # 用 IP 直接登录(此时还没免密)

# 登录后在服务器上执行:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "粘贴你的公钥内容" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

6.3 排查:为什么还在要密码?

如果上传公钥后仍然要求输密码,通常是以下原因之一:

bash 复制代码
# 在服务器上检查权限
ls -la ~/.ssh/
# .ssh 目录权限必须是 700(drwx------)
# authorized_keys 文件权限必须是 600(-rw-------)

# 检查 SSH 服务端配置(需要 root 权限)
grep -E "PubkeyAuthentication|AuthorizedKeysFile" /etc/ssh/sshd_config
# 确认 PubkeyAuthentication yes 没有被注释掉

7. 第四步:配置 iTerm2 Profile

配置好 Profile 后,打开 iTerm2 → Cmd+O 搜索服务器名 → 直接连接,和 Xshell 双击服务器的体验一样。

7.1 新建 Profile

  1. 打开 iTerm2,按 Cmd ,(逗号)打开偏好设置
  2. 点击左侧 Profiles 标签
  3. 点击左下角 + 新建一个 Profile

7.2 配置 General 标签

  • Name :起一个好认的名字,比如 myserver - 生产环境
  • Command :选择 Command,输入框里填:
bash 复制代码
ssh myserver

如果你想连接后自动进入某个目录,可以:

bash 复制代码
ssh myserver -t 'cd /var/www && exec $SHELL'

7.3 配置 Colors 标签(可选,区分不同服务器)

给不同的服务器设置不同的背景颜色,是个很实用的习惯------一眼就能看出自己在哪台机器上,避免误操作:

  • 生产环境:深红色背景 (#1a0000),时刻提醒自己"谨慎操作"
  • 测试环境:深蓝色背景 (#00001a)
  • 开发环境:默认黑色

设置方法:Colors → Color Presets → 下拉选择或自定义背景色(Background)

7.4 使用 Profile 连接

配置完成后,两种方式打开:

  • Cmd+O:打开 Profile 搜索框,模糊搜索服务器名,回车连接
  • Cmd+N:在弹出的窗口选择 Profile

8. 第五步:配置文件传输函数

把以下内容追加到 ~/.zshrc 文件末尾,然后重载配置。

8.1 打开 zshrc

bash 复制代码
open -e ~/.zshrc

在文件最末尾加入以下内容:

8.2 文件传输函数完整代码

bash 复制代码
# ============================================================
# SSH 文件传输快捷函数
# 前提:~/.ssh/config 中已配置好 Host 别名(如 myserver)
# ============================================================

# ------ scp:适合单个文件,简单直接 ------

# 上传文件
# 用法: put 本地文件路径 [远程目录,默认家目录]
put() {
  if [ -z "$1" ]; then
    echo "用法: put <本地文件路径> [远程目录]"
    echo "示例: put ~/Downloads/app.jar /opt/myapp"
    return 1
  fi
  local file="$1"
  local remote_dir="${2:-~}"
  echo "正在上传 $file → myserver:$remote_dir"
  scp "$file" myserver:"$remote_dir" && echo "✓ 上传完成"
}

# 下载文件
# 用法: get 远程文件路径 [本地存放目录,默认当前目录]
get() {
  if [ -z "$1" ]; then
    echo "用法: get <远程文件路径> [本地目录]"
    echo "示例: get /var/log/app.log ~/Desktop"
    return 1
  fi
  local remote_file="$1"
  local local_dir="${2:-.}"
  echo "正在下载 myserver:$remote_file → $local_dir"
  scp myserver:"$remote_file" "$local_dir" && echo "✓ 下载完成"
}

# 上传整个文件夹(递归)
# 用法: put-dir 本地文件夹路径 [远程目录,默认家目录]
put-dir() {
  if [ -z "$1" ]; then
    echo "用法: put-dir <本地文件夹路径> [远程目录]"
    return 1
  fi
  echo "正在上传目录 $1 → myserver:${2:-~}"
  scp -r "$1" myserver:"${2:-~}" && echo "✓ 目录上传完成"
}

# 下载整个文件夹(递归)
# 用法: get-dir 远程文件夹路径 [本地目录,默认当前目录]
get-dir() {
  if [ -z "$1" ]; then
    echo "用法: get-dir <远程文件夹路径> [本地目录]"
    return 1
  fi
  echo "正在下载目录 myserver:$1 → ${2:-.}"
  scp -r myserver:"$1" "${2:-.}" && echo "✓ 目录下载完成"
}

# ------ rsync:适合反复同步,只传变化部分,速度更快 ------
# 参数说明:
#   -a  归档模式(保留权限、时间戳、符号链接等)
#   -v  显示传输的文件列表
#   -z  传输时压缩(对文本文件效果好)
#   --progress  显示每个文件的进度条

# 上传并同步(本地 → 远程)
# 用法: sync-up 本地路径 [远程目录,默认家目录]
sync-up() {
  if [ -z "$1" ]; then
    echo "用法: sync-up <本地路径> [远程目录]"
    echo "示例: sync-up ./dist /var/www/myapp"
    return 1
  fi
  rsync -avz --progress "$1" myserver:"${2:-~}"
}

# 下载并同步(远程 → 本地)
# 用法: sync-down 远程路径 [本地目录,默认当前目录]
sync-down() {
  if [ -z "$1" ]; then
    echo "用法: sync-down <远程路径> [本地目录]"
    echo "示例: sync-down /var/log/myapp ~/local-logs"
    return 1
  fi
  rsync -avz --progress myserver:"$1" "${2:-.}"
}

8.3 使配置生效

bash 复制代码
source ~/.zshrc

验证函数已加载:

bash 复制代码
type put
# 应该输出函数定义内容,而不是 "not found"

9. 日常使用示例

9.1 连接服务器

bash 复制代码
# 方式一:直接在终端里连
ssh myserver

# 方式二:iTerm2 里用 Cmd+O 搜索 Profile 名一键连接

9.2 上传文件

bash 复制代码
# 上传单个文件到服务器家目录
put ~/Downloads/config.json

# 上传单个文件到指定目录
put ~/Downloads/app-v2.jar /opt/myapp/

# 上传整个文件夹
put-dir ~/projects/frontend /var/www/

# 用 rsync 同步目录(只传变化的部分,适合反复部署)
sync-up ./build /var/www/myapp

9.3 下载文件

bash 复制代码
# 下载日志文件到当前目录
get /var/log/nginx/error.log

# 下载配置文件到桌面
get /etc/nginx/nginx.conf ~/Desktop/

# 下载整个日志目录
get-dir /var/log/myapp ~/local-logs/

# 用 rsync 同步(适合增量备份)
sync-down /var/www/myapp/uploads ~/backups/uploads

9.4 在 Shell 里直接用 Tab 补全远程路径

配合 ~/.ssh/configscp 命令本身支持 Tab 补全远程路径(需要先连接过一次,或使用 zsh 的 ssh 补全插件),比如:

bash 复制代码
scp myserver:/var/log/<Tab>  # 可以补全远程目录

10. 多台服务器的管理技巧

10.1 在 ~/.ssh/config 里加更多服务器

格式完全相同,一台服务器一个 Host 块,别名自己起:

bash 复制代码
Host prod
    HostName 111.222.333.444
    User deploy
    Port 22
    IdentityFile ~/.ssh/id_ed25519

Host staging
    HostName 192.168.1.100
    User ubuntu
    Port 22
    IdentityFile ~/.ssh/id_ed25519

Host db-server
    HostName 10.0.0.5
    User root
    Port 22
    IdentityFile ~/.ssh/id_ed25519

10.2 给每台服务器加文件传输函数

~/.zshrc 里,对每台服务器复制一套函数,修改函数名和 Host 别名即可:

bash 复制代码
# 针对 prod 服务器
put-prod() { scp "$1" prod:"${2:-~}"; }
get-prod() { scp prod:"$1" "${2:-.}"; }
sync-prod() { rsync -avz --progress "$1" prod:"${2:-~}"; }

# 针对 staging 服务器
put-staging() { scp "$1" staging:"${2:-~}"; }
get-staging() { scp staging:"$1" "${2:-.}"; }
sync-staging() { rsync -avz --progress "$1" staging:"${2:-~}"; }

10.3 通用版本:支持传入服务器名

如果服务器多了,也可以写一个通用函数,第一个参数传服务器名:

bash 复制代码
# 通用上传:transfer-put 服务器别名 本地文件 [远程目录]
transfer-put() {
  local server="$1"
  local file="$2"
  local remote_dir="${3:-~}"
  if [ -z "$server" ] || [ -z "$file" ]; then
    echo "用法: transfer-put <服务器别名> <本地文件> [远程目录]"
    return 1
  fi
  scp "$file" "$server":"$remote_dir"
}

# 用法示例:
# transfer-put prod ~/app.jar /opt/
# transfer-put staging ~/config.json /etc/myapp/

10.4 用 iTerm2 的 Profiles 分组管理

在 iTerm2 Profiles 里,每台服务器对应一个 Profile,Tags 字段可以用来分组(比如 productionstagingpersonal),Cmd+O 搜索框支持按 Tag 过滤。


11. 常见问题排查

问题 1:Permission denied (publickey) --- 公钥认证失败

排查步骤:

bash 复制代码
# 加 -v 参数查看详细调试信息
ssh -v myserver

# 重点看这几行:
# "Offering public key: ~/.ssh/id_ed25519"  ← 有这行说明本地在尝试用密钥
# "Authentications that can continue: publickey"  ← 服务器支持密钥认证
# "Permission denied"  ← 服务器拒绝了这个密钥

常见原因和解决方法:

bash 复制代码
# 原因 1:服务器上 .ssh 目录或 authorized_keys 权限不对
# 在服务器上执行:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# 原因 2:公钥没有正确添加到 authorized_keys
# 在服务器上检查:
cat ~/.ssh/authorized_keys
# 确认里面有你的公钥内容(以 "ssh-ed25519" 开头的那一行)

# 原因 3:本地私钥路径写错了
# 检查 ~/.ssh/config 里的 IdentityFile 路径是否存在
ls -la ~/.ssh/

问题 2:Connection refused --- 连不上服务器

bash 复制代码
# 先确认网络是否通
ping 1.2.3.4

# 确认 SSH 端口是否开放(默认 22)
nc -zv 1.2.3.4 22
# 出现 "succeeded" 表示端口可达

# 如果端口不是 22,确认 ~/.ssh/config 里 Port 填的是否正确

问题 3:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

这个警告说明服务器的 SSH 指纹变了(常见于服务器重装系统后)。如果你确认是自己的服务器且确实重装过,可以删掉旧记录:

bash 复制代码
ssh-keygen -R myserver
# 或者指定 IP:
ssh-keygen -R 1.2.3.4

然后重新连接,确认新指纹后会自动记录。

⚠️ 如果你没有重装服务器,这个警告可能意味着中间人攻击,不要轻易忽略,先联系服务器管理员确认。

问题 4:scprsync 命令提示找不到

scp 是系统自带的,通常不会缺失。rsync 在新版 macOS 上可能需要单独安装:

bash 复制代码
brew install rsync

问题 5:长时间不操作后连接断了

~/.ssh/configHost * 块里加入 Keep-alive 设置(本文第 5 步已包含):

复制代码
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

这会让 SSH 客户端每 60 秒发一次心跳包,防止服务器因为空闲超时把你踢掉。

问题 6:~/.ssh/config 不生效

最常见的原因是文件权限不对:

bash 复制代码
# 检查权限
ls -la ~/.ssh/config
# 必须是 -rw------- (即 600)

# 修复权限
chmod 600 ~/.ssh/config

12. 完整配置汇总

把这个章节当做"完整模板",按需复制修改。

~/.ssh/config

bash 复制代码
# ============ 服务器 1 ============
Host myserver
    HostName 1.2.3.4        # ← 改成你的服务器 IP 或域名
    User root                # ← 改成你的用户名
    Port 22
    IdentityFile ~/.ssh/id_ed25519

# ============ 全局设置 ============
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 10m

~/.zshrc 末尾追加部分

bash 复制代码
# ============ SSH 文件传输函数 ============

put() {
  [ -z "$1" ] && echo "用法: put <本地文件> [远程目录]" && return 1
  scp "$1" myserver:"${2:-~}" && echo "✓ 上传完成"
}

get() {
  [ -z "$1" ] && echo "用法: get <远程文件> [本地目录]" && return 1
  scp myserver:"$1" "${2:-.}" && echo "✓ 下载完成"
}

put-dir() {
  [ -z "$1" ] && echo "用法: put-dir <本地目录> [远程目录]" && return 1
  scp -r "$1" myserver:"${2:-~}" && echo "✓ 目录上传完成"
}

get-dir() {
  [ -z "$1" ] && echo "用法: get-dir <远程目录> [本地目录]" && return 1
  scp -r myserver:"$1" "${2:-.}" && echo "✓ 目录下载完成"
}

sync-up() {
  [ -z "$1" ] && echo "用法: sync-up <本地路径> [远程目录]" && return 1
  rsync -avz --progress "$1" myserver:"${2:-~}"
}

sync-down() {
  [ -z "$1" ] && echo "用法: sync-down <远程路径> [本地目录]" && return 1
  rsync -avz --progress myserver:"$1" "${2:-.}"
}

快速安装脚本

如果你想一次性把以上配置都跑完(请先阅读内容,确认符合你的情况再执行):

bash 复制代码
# 1. 生成密钥(如果还没有)
[ ! -f ~/.ssh/id_ed25519 ] && ssh-keygen -t ed25519 -C "$(whoami)@mac"

# 2. 设置权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519

# 3. 创建 config 文件(如果还没有)
touch ~/.ssh/config && chmod 600 ~/.ssh/config

# 4. 把公钥复制到剪贴板(手动添加到服务器)
cat ~/.ssh/id_ed25519.pub | pbcopy
echo "✓ 公钥已复制到剪贴板,请手动添加到服务器的 ~/.ssh/authorized_keys"

总结

这套方案的核心理念是用 macOS 已有的工具做好一件事,不引入不必要的依赖:

  • ~/.ssh/config 是连接配置的唯一来源,ssh/scp/rsync 共用,不用到处填 IP
  • iTerm2 Profile 把"搜索 + 一键连接"的体验做到和 Xshell 一样顺手
  • Shell 函数把上传/下载变成几个字母,比打开 GUI 文件管理器还快
  • Keep-alive + 连接复用确保体验稳定,不会频繁断线

配置完成后,日常最高频的两个操作------连接服务器传文件------都是一条命令或一次按键的事。