本文目录导读:

在脚本中通过SSH远程执行命令,主要有以下几种方式,它们的核心区别在于是否需要交互、是否占用终端以及如何传递密码。
核心方式对比
| 方式 | 核心命令 / 工具 | 是否需要交互 | 密码处理 | 典型场景 |
|---|---|---|---|---|
| 直接命令模式 | ssh user@host command |
否 | 需密钥或expect | 执行单条或简单命令 |
| 本地脚本管道 | cat script.sh \| ssh user@host |
否 | 需密钥或expect | 执行较复杂的脚本逻辑 |
| 远程执行文件 | scp + ssh 组合 |
否 | 需密钥或expect | 依赖本地文件或需要持久化脚本 |
| 交互式自动化 | expect / sshpass |
是 | 明文或自动输入 | 环境不支持密钥,必须密码登录 |
| 高级工具封装 | Ansible, Fabric, Paramiko | 否 | 多种方式 | 大批量、复杂编排、需要幂等性 |
直接命令模式(最常用)
直接在 ssh 命令后面加上要执行的命令,SSH 会执行完毕后自动退出。
# 基础语法 ssh user@host "command1; command2; command3" # 示例:查看远程磁盘空间 ssh root@192.168.1.100 "df -h" # 示例:在远程执行带变量的命令(需注意引号转义) ssh user@host 'echo "Hostname: $(hostname); Date: $(date)"' # 示例:在远程执行需要 sudo 权限的命令 ssh -t user@host "sudo systemctl restart nginx" # -t 参数强制分配伪终端,解决 sudo 可能提示 "no tty present" 的问题
优缺点:
- ✅ 简单直接,无需额外脚本。
- ❌ 复杂逻辑(if/for 循环)写起来繁琐,转义复杂。
本地脚本管道模式(推荐较复杂逻辑)
将本地脚本文件的内容通过管道发送到远程主机的 shell 中执行。
# 本地脚本 local_script.sh
#!/bin/bash
echo "=== System Info ==="
uname -a
echo "=== Disk Usage ==="
df -h
# 通过管道远程执行
cat local_script.sh | ssh user@host bash
# 也可以直接使用 heredoc(无需单独脚本文件)
ssh user@host bash << 'EOF'
for i in {1..5}; do
echo "Iteration $i"
uptime
done
EOF
# 注意:EOF 用单引号括起来表示禁止本地shell解析变量,否则会先被本地替换
优缺点:
- ✅ 适合执行多步、含循环/判断的复杂逻辑。
- ❌ 脚本标准错误和标准输出混合,调试稍微困难(可通过重定向解决)。
远程执行文件模式(依赖本地文件)
先将本地脚本或文件上传到远程主机,再通过 SSH 执行该文件。
# 1. 上传脚本 scp /path/to/script.sh user@host:/tmp/script.sh # 2. 远程执行 ssh user@host "bash /tmp/script.sh" # 3. 清理远程文件(可选) ssh user@host "rm /tmp/script.sh" # 或者合并为一行 scp script.sh user@host:/tmp/script.sh && ssh user@host "bash /tmp/script.sh && rm /tmp/script.sh"
优缺点:
- ✅ 脚本可以依赖本地附件,执行结果可控,适合批量分发。
- ❌ 多了一步上传操作,稍微慢一点。
交互式自动化模式(处理密码登录)
当无法配置 SSH 密钥时,需要自动输入密码。
方式 A:使用 sshpass(最简单)
# 安装:apt install sshpass / yum install sshpass sshpass -p 'your_password' ssh user@host "command" # 注意:密码明文,安全性较低,不推荐生产环境 # 可通过环境变量稍微提高安全性(但仍不完美) export SSHPASS='your_password' sshpass -e ssh user@host "command"
方式 B:使用 expect(最灵活)
#!/usr/bin/expect set timeout 10 spawn ssh user@host expect "password:" send "your_password\r" expect "$ " send "ls -la\r" expect "$ " send "exit\r" expect eof
优缺点:
- ✅ 能解决一切交互式登录问题,特别适合跳板机、二次认证等场景。
- ❌
sshpass密码明文,expect脚本维护成本较高。
高级工具封装模式(批量与编排)
Ansible(无需 agent,SSH 底层)
- name: 远程执行命令
hosts: webservers
tasks:
- name: Check disk usage
shell: df -h
register: result
- debug: var=result.stdout_lines
ansible webservers -m shell -a "df -h"
Fabric(Python 库)
from fabric import Connection
with Connection("user@host") as c:
result = c.run("uname -a", hide=True)
print(result.stdout)
Paramiko(纯 Python SSH 库)
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect("host", username="user", password="pass")
stdin, stdout, stderr = client.exec_command("df -h")
print(stdout.read().decode())
client.close()
核心注意事项
- 优先使用 SSH 密钥登录,避免密码明文出现在脚本或日志中。
- 环境变量差异:通过
ssh执行命令时,不会加载.bashrc或.profile,需手动指定或使用source ~/.bashrc。ssh user@host "source ~/.bashrc && command"
- 引号转义:复杂命令建议用单引号包裹整个远程命令,防止本地 shell 先解析。
- 本地变量:
ssh user@host "echo $HOME"(输出本地 HOME) - 远程变量:
ssh user@host 'echo $HOME'(输出远程 HOME)
- 本地变量:
- 标准输入重定向:如果脚本需要通过 STDIN 接收输入,需注意管道和 SSH 的 STDIN 冲突。
- 错误处理:默认 SSH 远程命令返回码仅代表远程命令的退出码,建议在脚本中加上
set -e或检查 。 - 多命令执行顺序:使用 、
&&、 控制逻辑,用 或 分组。
选择建议
| 场景 | 推荐方式 |
|---|---|
| 简单单条命令 | 方式1:直接 ssh user@host "command" |
| 复杂循环/条件逻辑 | 方式2:本地脚本管道 cat script \| ssh ... |
| 需要上传文件后执行 | 方式3:scp + ssh 组合 |
| 无法使用密钥,必须密码 | 方式4:sshpass(低安全)或 expect(高控制) |
| 批量管理大量服务器 | 方式5:Ansible |
| 需要集成到 Python 应用 | 方式5:Paramiko / Fabric |