Appearance
04|文件传输
本机写好的配置要传到服务器、服务器上的日志要拉回来分析、发布包要推到多台机器——远程传文件是高频操作。场景不同,工具也不同:
| 工具 | 适用场景 | 特点 |
|---|---|---|
| scp | 临时传一两个文件 | 命令还在,但底层协议已换(见下文) |
| sftp | 交互式浏览后传文件 | 配合批处理可脚本化 |
| rsync | 增量同步、大目录、断点续传 | 比对差异后只传变化部分 |
| lrzsz | 终端里临时拖个小文件 | 依赖终端客户端,ZMODEM |
本篇讲这几种工具怎么用、各自适合什么场景,以及传输时容易踩的坑。
一、传文件前的共性确认
传文件之前确认三件事,避免一半以上的低级错误:
bash
# 1. 目标目录是否存在?
ssh user@host 'ls -d /etc/nginx/conf.d'
# 不存在时,scp 会把文件写到 /etc/nginx/conf.d 这个"文件"而非目录内
# 2. 目标用户有写权限?
# Permission denied 在连上后发生,说明文件系统权限不够
# 3. 会不会静默覆盖?
# scp 默认直接覆盖,没有任何提示二、scp
scp 走 SSH 加密,用法和 cp 类似,只是路径可跨机器。
scp 的协议已经换了
从 OpenSSH 9.0(2022 年)开始,scp 命令底层不再用老的 SCP 协议,改用 SFTP 协议。原因:老 SCP 协议有安全问题(CVE-2020-15778),且很难在不破坏兼容性的前提下修复。
实际影响:
scp命令照常用,写法和以前一样,不用改- 底层走的是 SFTP,比老协议安全
- RHEL 9 等新系统默认就这么跑
- 老的 SCP 协议还能用
-O强制启用,但一般没必要
所以"scp 不能用了"是误传——命令还在,只是底层换了更安全的实现。日常临时传文件,scp 仍然是最顺手的。
基本用法
基本用法
bash
# 上传单个文件
scp ./app.conf root@192.168.10.129:/etc/myapp/app.conf
# 下载
scp root@192.168.10.129:/var/log/messages ./messages
# 指定端口和私钥(注意:-P 大写)
scp -P 2222 -i ~/.ssh/id_ed25519_ops ./app.conf root@host:/tmp/scp -P 是大写;ssh -p 是小写。 scp -p(小写)是保留文件属性,不是指定端口。
常用参数
bash
scp -r ./dist root@host:/opt/myapp/ # 递归目录
scp -p ./backup.tar.gz root@host:/backup/ # 保留时间戳和权限
scp -l 20480 ./bigfile root@host:/data/ # 限速(Kbit/s)
scp -C ./logs.tar root@host:/tmp/ # 启用压缩-l 20480 约等于 20 Mbit/s。跨机房大文件不加限速可能打满链路影响线上服务。
覆盖风险
scp 覆盖同名文件无任何提示:
bash
# 先备份远端文件
ssh root@host 'cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak'
# 再传新文件
scp ./nginx.conf root@host:/etc/nginx/nginx.conf三、sftp
sftp 提供交互式文件传输客户端:
bash
sftp root@192.168.10.129进入后常用命令:
| 命令 | 作用 |
|---|---|
pwd / lpwd | 看远端/本地目录 |
ls / lls | 列远端/本地文件 |
cd / lcd | 切远端/本地目录 |
put <file> | 上传 |
get <file> | 下载 |
bye | 退出 |
命令前加 l(local)操作本地,不加操作远端。
批处理模式
bash
cat > sftp-batch.txt <<'EOF'
cd /tmp
put app.tar.gz
put app.conf
bye
EOF
sftp -b sftp-batch.txt root@192.168.10.129四、rsync
rsync 的核心是增量同步:对比源端和目标端,只传变化的内容。
基本用法
bash
# 本地同步到远端
rsync -av ./dist/ root@192.168.10.129:/opt/myapp/dist/
# 远端拉回本地
rsync -avP root@host:/var/log/nginx/ ./nginx-logs/常用参数
| 参数 | 作用 |
|---|---|
-a | archive 模式:递归+保留权限/时间戳/软链接/属主 |
-v | 显示传输文件列表 |
-z | 传输时压缩(文本类文件有效) |
-P | --partial --progress:断点续传+进度 |
--delete | 让目标端与源端完全一致,删除目标端多出来的文件 |
--dry-run | 预演,列出变更但不执行 |
-e 'ssh -p 2222' | 指定 ssh 端口和参数 |
路径斜杠陷阱
rsync 源路径末尾的斜杠极其重要:
bash
# 带斜杠:复制 dist/ 下所有内容到 /opt/app/
rsync -av ./dist/ host:/opt/app/
# → /opt/app/file1, /opt/app/file2
# 不带斜杠:复制 dist 目录本身到 /opt/app/
rsync -av ./dist host:/opt/app/
# → /opt/app/dist/file1, /opt/app/dist/file2带斜杠=复制内容,不带斜杠=复制目录本身。未确认时先用 --dry-run 预览:
bash
rsync -avP --dry-run ./dist/ root@host:/opt/myapp/dist/--delete 的危险性
--delete 让目标端与源端完全一致,目标端多出来的文件会被删除:
bash
rsync -avP --delete ./dist/ root@host:/opt/myapp/dist/源端如果写错了(空目录、挂载失败),目标端的内容会被清空。 --dry-run 的目的不只是确认目录层级,还包括确认源端内容正确。
文件属性保留
rsync -a 尽量保留属性,但属主/属组能否保留取决于执行用户有没有权限:
bash
# 非 root 用户同步过去,属主变成登录用户
rsync -av ./ scripts/ user@host:/opt/scripts/
# 显式设置目标端权限(Web 静态文件发布场景)
rsync -av --chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r ./dist/ host:/opt/app/五、传输校验
关键文件传输后做校验,确认没有损坏。TCP 校验和较弱,应用层 SHA-256 是对抗静默数据损坏的简单手段:
bash
# 源端生成哈希
sha256sum app.tar.gz > app.tar.gz.sha256
# 传输
scp app.tar.gz app.tar.gz.sha256 root@host:/tmp/
# 远端校验
ssh root@host 'cd /tmp && sha256sum -c app.tar.gz.sha256'
# 期望:app.tar.gz: OK
# 或下载后将本地生成的哈希与远端源文件对比
ssh root@host 'sha256sum /backup/db_export.sql' | diff - <(sha256sum ./db_export.sql)发布包、备份包、离线安装包,都要加传输校验。
六、堡垒机文件传输
有堡垒机后,文件传输方式取决于授权策略。
如果堡垒机只允许 Web 文件管理,自己再开 SCP 直连目标机器等于绕过审计。
| 方式 | 说明 |
|---|---|
| Web SFTP | 浏览器中上传/下载,走审计 |
| 客户端 SFTP | 通过堡垒机代理连接 |
| 审批+分发 | 先工单审批,由堡垒机分发到目标 |
JumpServer 里的授权规则要看两个设置:是否允许文件传输(部分授权只给"连接"),下载是否有审计记录(数据带出操作必须可追溯)。
七、lrzsz(终端传小文件)
bash
yum install lrzsz -y
apt install lrzsz -y
# 上传:弹出文件选择框
rz
# 下载:弹出保存路径
sz /tmp/debug.log依赖终端客户端支持 ZMODEM,Xshell、SecureCRT、iTerm2 支持;Web Terminal、VS Code 内置终端等不支持。
只适合临时传小文件。正式发布包走 rsync 或堡垒机。
八、常见问题
Permission denied(两段区分)
text
# SSH 认证层拒绝——没连上
Permission denied (publickey,password).
# 文件系统层拒绝——连上了但写不了
scp: dest open "/etc/myapp/app.conf": Permission denied文件系统层拒绝的处理:
bash
# 先传到 /tmp,再 sudo 移动
scp ./app.conf user@host:/tmp/app.conf
ssh user@host 'sudo install -m 0644 -o root -g root /tmp/app.conf /etc/myapp/app.conf'install 命令比 mv 更安全——移动的同时设置权限和属主。
传输中断
bash
# scp 中断→重头传。大文件用 rsync -P
rsync -avP ./bigfile.tar.gz root@host:/data/-P 保留已接收的部分,重跑自动续传。
文件名空格
服务器上命名用 - 或 _ 替代空格,避免引号和转义问题:
bash
scp "./app config.conf" root@host:/tmp/Windows 脚本的换行符
bash
# Windows 编辑的脚本传到 Linux:开头 /bin/bash^M → bad interpreter
# 查看行尾
file deploy.sh
# 转换
dos2unix deploy.sh传输前先备份
覆盖配置前,先在远端用时间戳备份:
bash
ssh root@host 'cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%F-%H%M%S).bak'