Linux 中 SSH 远程执行命令:从基础到实战
概述
SSH(Secure Shell,安全外壳协议)是个跑在应用层的加密网络协议,核心价值就是给远程登录、传文件、执行命令这些操作上一道端到端的保险——靠非对称加密、数据完整性校验这些机制,远程管理时不用担心信息被偷看、被篡改。
最早 SSH 只是 UNIX 系统上的远程管理工具,现在已经是跨平台(Linux、MacOS、Windows 都支持)的标准远程协议了。除了最常用的“远程登录服务器”,SSH 最厉害的一点其实是能直接在远端跑命令 / 脚本,批量配置服务器、跑定时任务这类自动化运维,全靠它撑着。
1. 远程执行单行 / 多行命令
适合命令逻辑简单、不用交互的场景(比如看下服务器状态、建个文件),关键就是用引号把多条命令包起来,再用分号(;)决定谁先谁后跑。
语法格式
# 单行命令(直接执行)
ssh $username@$ip "single_command"
# 多行命令(用分号分隔,按顺序执行)
ssh $username@$ip "command1; command2; command3"
实战示例
# 1. 远程查看目标服务器的系统版本(单行命令)
ssh root@192.168.1.100 "cat /etc/os-release"
# 2. 远程创建目录→新建文件→写入内容(多行命令)
ssh roger@192.168.1.101 "mkdir -p /home/roger/test; cd /home/roger/test; echo 'SSH remote test' > test.txt"
注意事项
- 若命令中包含特殊字符(如 $、*、>),需用单引号包裹命令,避免本地 Shell 提前解析变量(例如:ssh user@ip ‘echo $PATH’ 会输出远程服务器的 PATH,而双引号会输出本地 PATH)。
- 分号(;)表示 “无论前一条命令是否成功,都执行下一条”;若需 “前一条成功才执行下一条”,可改用 &&(例如:command1 && command2)。
2. 远程执行交互命令
碰到命令要跟人交互的情况(比如 sudo 输密码、vim 改文件、top 看进程),直接跑会因为“没有终端交互环境”而失败,这时候得加 -t 参数,强制给 SSH 会话分配一个伪终端(pseudo-tty)。
语法格式
ssh -t $username@$ip "interactive_command"
实战示例
# 1. 远程执行 sudo 命令(需输入密码交互)
ssh -t roger@192.168.1.100 "sudo apt update"
# 2. 远程使用 top 查看进程(需终端交互操作)
ssh -t root@192.168.1.101 "top"
关键说明
- -t 参数的作用是 “告诉 SSH 客户端:需要模拟一个本地终端,让远程命令能获取到交互输入”,若无此参数,sudo 会提示 “抱歉,无法通过 sudo 获得密码,因为没有终端”。
- 若需多次交互(如连续执行多个需要密码的命令),可添加 -tt(双 -t)参数,确保终端交互的稳定性。
3. 远程执行命令时传入本地变量
想把本地 Shell 变量(比如本地定义的文件名、时间戳)带进远程命令里时,得注意变量是在哪一层解析的,别让本地 Shell 提前把它替换掉了。
语法格式(两种可靠方式)
# 方式1:用 bash -c 接收命令,本地变量用双引号包裹(推荐)
local_var="hello_ssh"
ssh $username@$ip bash -c "'echo $local_var'"
# 方式2:用单引号包裹远程命令,变量部分用双引号拆分(更直观)
ssh $username@$ip 'echo "'$local_var'"'
实战示例
# 本地定义文件名称,远程创建对应文件
file_name="remote_file_$(date +%Y%m%d).txt"
ssh roger@192.168.1.100 bash -c "'touch /home/roger/$file_name'"
# 执行后远程会生成类似 "remote_file_20240520.txt" 的文件
常见坑点
- 要是直接写 ssh user@ip “echo $local_var”,本地 Shell 会先把 $local_var 换成本地的值,再传给远程,表面上能用,可一旦命令里带循环、条件判断这种复杂逻辑,解析就乱套了,所以一律推荐用 bash -c 的方式。
4. 远程执行本地脚本(不带参数)
命令逻辑一复杂(比如多步部署、批量处理),把命令硬写在 SSH 后面就不好维护了。这时候可以把逻辑塞进一个本地脚本,直接用 SSH 传给远端跑,连上传脚本都省了。
语法格式
# 本地脚本路径为 local_script.sh,通过输入重定向(<)传递给远程
ssh $username@$ip < local_script.sh
实战示例
- 本地创建脚本 local_deploy.sh(示例:远程安装 Nginx 依赖):
#!/bin/bash
# 本地脚本:远程执行安装依赖操作
echo "开始在远程服务器安装依赖..."
sudo apt update && sudo apt install -y gcc g++ make libpcre3-dev zlib1g-dev
echo "依赖安装完成!"
- 赋予脚本执行权限并远程执行:
# 本地赋予权限
chmod +x local_deploy.sh
# 远程执行本地脚本(无需上传脚本到远程)
ssh -t roger@192.168.1.100 < local_deploy.sh
关键说明
- 脚本开头必须指定解释器(如 #!/bin/bash),否则远程服务器可能用默认的 sh 执行,导致语法兼容问题(如 [[]] 表达式在 sh 中不支持)。
- 若脚本包含 sudo 等交互命令,需搭配 -t 参数(参考 “远程执行交互式命令” 章节)。
5. 远程执行本地脚本(带参数)
要是本地脚本还得动态传参数(比如环境变量、目标路径),那就在传脚本的同时,用 bash -s 让远端的 Bash 去接这些参数(-s 的意思是“从标准输入读脚本内容”,参数跟在后面传进去)。
语法格式
# local_script.sh 是本地脚本,$param1 $param2 是要传递的参数
ssh $username@$ip 'bash -s' < local_script.sh $param1 $param2
实战示例
- 本地创建带参数的脚本 local_backup.sh(示例:远程备份指定目录):
#!/bin/bash
# 本地脚本:接收2个参数(备份源目录、备份目标路径)
src_dir=$1 # 第1个参数:要备份的目录
dest_path=$2 # 第2个参数:备份文件保存路径
echo "开始备份远程目录 $src_dir ..."
tar -zcf $dest_path/backup_$(date +%H%M%S).tar.gz $src_dir
echo "备份完成!文件保存至 $dest_path"
- 本地执行远程脚本并传参:
# 传递参数:1. 远程要备份的目录 /var/log;2. 备份保存路径 /home/roger/backups
ssh -t roger@192.168.1.100 'bash -s' < local_backup.sh /var/log /home/roger/backups
6. 远程执行远程服务器上的脚本(不带参数)
脚本要是早就放到远端了(比如用 CI/CD 传上去的、或者手动丢过去的),直接 SSH 指定那个脚本的绝对路径就能跑,不用再传本地文件。
语法格式
# 必须指定远程脚本的绝对路径(如 /home/roger/remote_script.sh)
ssh $username@$ip /path/to/remote_script.sh
实战示例
# 远程脚本已存在于 /home/roger/clean_logs.sh(功能:清理7天前的日志)
ssh roger@192.168.1.100 /home/roger/clean_logs.sh
注意事项
- 必须用绝对路径:远程 Shell 的 PATH 环境变量可能不包含脚本所在目录(如脚本在用户家目录,但 PATH 未包含 ~/),直接写脚本名会提示 “命令不存在”。
- 确保脚本有执行权限:若远程脚本无权限,需先执行 ssh user@ip “chmod +x /path/to/remote_script.sh” 赋予权限。
7. 远程执行远程服务器上的脚本(带参数)
跟“本地脚本传参”差不多,远端脚本接参数时,直接在脚本路径后面把参数跟上就行,远端 Bash 会自动把参数递给脚本。
语法格式
# 远程脚本路径 + 空格 + 参数1 + 空格 + 参数2
ssh $username@$ip /path/to/remote_script.sh $param1 $param2
实战示例
- 远程脚本 remote_copy.sh(已存在于 /home/roger/,功能:复制文件到指定目录):
#!/bin/bash
# 远程脚本:接收2个参数(源文件、目标目录)
src_file=$1
dest_dir=$2
if [ -f $src_file ]; then
cp $src_file $dest_dir
echo "文件 $src_file 已复制到 $dest_dir"
else
echo "错误:源文件 $src_file 不存在!"
fi
- 本地执行远程脚本并传参:
# 传递参数:1. 远程源文件 /home/roger/test.txt;2. 目标目录 /tmp
ssh roger@192.168.1.100 /home/roger/remote_copy.sh /home/roger/test.txt /tmp
总结
SSH 远程跑命令 / 脚本是 Linux 运维的硬核技能,不同场景对应不同的搞法,核心就一句:先想清楚“命令 / 脚本在本地还是远端”“要不要交互 / 传参”。下面这张表把场景和方案对上了,直接照着用:
| 需求场景 | 推荐方案 | 关键参数 / 注意点 |
|---|---|---|
| 简单单行 / 多行命令(无交互) | ssh user@ip “cmd1; cmd2” | 复杂命令用单引号,避免本地解析 |
| 交互式命令(如 sudo、top) | ssh -t user@ip “interactive_cmd” | 必须加 -t 分配伪终端 |
| 传递本地变量到远程 | ssh user@ip bash -c “‘echo $var’” | 用 bash -c 确保变量正确传递 |
| 执行本地脚本(无参数) | ssh user@ip < local_script.sh | 脚本需指定解释器(#!/bin/bash) |
| 执行本地脚本(带参数) | ssh user@ip ‘bash -s’ < local_script.sh param | bash -s 接收脚本,参数后置 |
| 执行远程已存在的脚本 | ssh user@ip /path/to/remote_script.sh | 必须用绝对路径,确保脚本有权限 |