Skip to content

文件传输

远程传输文件在运维里几乎每天都会遇到——上传配置文件、下载日志、同步发布包、备份拉取。场景不同,工具选择也不同。这篇文章梳理 scpsftprsynclrzsz 各自的适用场景和容易踩的坑。

一、三个工具,各管一摊

工具擅长什么为什么不适合别的事
scp临时传一两个文件不支持增量,重复传很多不变文件也全量
sftp交互式登录后浏览和取文件交互式,不好写脚本自动化
rsync增量同步、保留属性、断点续传只传一个几 KB 小文件没必要用它
lrzsz终端里临时拖一个小文件依赖终端客户端支持 ZMODEM,生产批量传输不适合

选择的关键区别在于:scpsftp 每次都是全量传完,rsync 会两边比对后只传差异。所以 scp 第一次传和第五次传代价一样,rsync 第二次以后的传输量可以小很多。

远程路径的写法:

text
user@host:/path/to/file

本地传到远端:

text
本地路径 -> user@host:远端路径

远端下载到本地:

text
user@host:远端路径 -> 本地路径

传文件之前确认三件事可以避免一半以上的低级错误:

检查项如果不检查
目标目录是否存在想传到 /etc/nginx/conf.d/,结果目录不存在就变成了把文件内容写进 /etc/nginx/conf.d 这个文件
目标用户是否有写权限权限不够直接 Permission denied,中间过程白费
会不会静默覆盖已有文件scp 默认直接覆盖,没有任何提示

二、scp

scp 走 SSH 加密,用法和 cp 很像,只是路径可以跨机器。

上传单个文件:

bash
scp ./app.conf root@192.168.10.129:/etc/myapp/app.conf

下载文件:

bash
scp root@192.168.10.129:/var/log/messages ./messages-192.168.10.129

指定端口和私钥:

bash
scp -P 2222 -i ~/.ssh/id_ed25519_ops ./app.conf root@192.168.10.129:/tmp/

scp 的端口参数是大写 -Pssh 是小写 -p。混到一起时容易敲错——scp -p 是保留文件属性,不是指定端口。

几个常用参数

复制整个目录:

bash
scp -r ./dist root@192.168.10.129:/opt/myapp/

-r 递归复制。但 scp 传大目录时不比较两边的差异——源端 500 个文件,目标端已经有 499 个完全一样的,scp 还是全部再传一遍。重复同步的场景用 rsyncscp -r 合理。

保留文件的时间戳和权限:

bash
scp -p ./backup.tar.gz root@192.168.10.129:/backup/

这里的 -p(小写)是 preserve,保留修改时间、访问时间和权限模式。

限速:

bash
scp -l 20480 ./bigfile.tar.gz root@192.168.10.129:/data/

-l 的单位是 Kbit/s,20480 约等于 20 Mbit/s。跨机房传大文件或走生产网段时,不加限速可能打满链路影响线上服务。

启用 SSH 传输压缩:

bash
scp -C ./logs.tar root@192.168.10.129:/tmp/

-C 让 SSH 在传输时压缩数据。对日志、文本、配置文件有效;对已经压缩过的 .tar.gz.zip、镜像层文件,效果不大还会白白消耗 CPU。

如果 ~/.ssh/config 里有别名,scp 可以省掉很多参数:

Host test-rocky
    HostName 192.168.10.129
    User root
    IdentityFile ~/.ssh/id_ed25519_ops
    IdentitiesOnly yes
bash
scp ./app.conf test-rocky:/etc/myapp/

三、sftp

sftp 也是走 SSH,但它不一次性执行完退出,而是一个交互式客户端——连接进去后可以浏览、切换目录、上传、下载。

连接:

bash
sftp root@192.168.10.129

进去后的常用命令:

命令作用对应的本地操作
pwd显示远端当前目录
lpwd显示本地当前目录
ls列出远端文件ssh host ls
lls列出本地文件ls 本地
cd切换远端目录
lcd切换本地目录cd 本地
put上传文件
get下载文件
mkdir创建远端目录
rm删除远端文件
bye退出

一次完整会话:

text
sftp> lpwd
Local working directory: /home/ops/pkg

sftp> pwd
Remote working directory: /root

sftp> cd /tmp
sftp> put app.tar.gz
sftp> get remote.log
sftp> bye

批处理模式用 -b 传入命令列表:

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

sftp 有断点续传命令 reputreget

text
sftp> reput bigfile.tar.gz        # 续传上传
sftp> reget remote-bigfile.tar.gz # 续传下载

但需要目标端已有部分文件且文件内容没被改过,不然续出来的文件是坏的。

sftp 的批处理模式适合简单固定的上传下载步骤。逻辑一复杂(条件判断、错误处理、增量判断)就吃力了,这时候直接用 rsync 比较好。

四、rsync 基础

rsync 的核心差异在增量:对比源端和目标端,只传变化的内容。文件没变就不传,文件变了只传差异部分。重复同步的场景下这个能力很重要。

安装:

bash
yum install rsync -y    # RHEL 系
apt install rsync -y    # Debian 系

本地同步到远端:

bash
rsync -av ./dist/ root@192.168.10.129:/opt/myapp/dist/

常用参数:

参数做什么
-aarchive 模式:递归 + 保留权限、时间戳、软链接、属主属组(权限允许时)
-v显示正在传输的文件列表
-z传输时压缩,适合文本类流量
-P等于 --partial --progress:保留未传完的文件(续传) + 显示进度
--delete删除目标端有但源端没有的文件
--dry-run预演,打印将要发生的变化,不实际执行

带进度和断点续传:

bash
rsync -avP ./large-dir/ root@192.168.10.129:/data/large-dir/

-P 里的 --partial 会保留已经传完的部分文件块。网络断了下次再跑可以接着传,不用从头开始。大文件跨网传输时这个能力比 scp 更实用。

远端拉回本地:

bash
rsync -avP root@192.168.10.129:/var/log/nginx/ ./nginx-logs/

通过非标准端口或指定私钥:

bash
rsync -avP -e 'ssh -p 2222' ./dist/ root@192.168.10.129:/opt/myapp/dist/

-e 指定底层用的远程 shell 程序。这里传的是 ssh -p 2222,rsync 会用这个命令来建立连接。

目录末尾斜杠的差别

rsync 的源路径末尾有没有斜杠,结果完全不一样:

命令结果
rsync -av ./dist/ host:/opt/app/dist 里面的内容同步到 /opt/app/
rsync -av ./dist host:/opt/app/dist 这个目录本身放到 /opt/app/ 下,变成 /opt/app/dist/

不带斜杠 = 复制目录本身。带斜杠 = 复制目录里的内容。这个细节容易搞混,发布目录时先用 --dry-run 看一眼输出确认层级没错:

bash
rsync -avP --dry-run ./dist/ root@192.168.10.129:/opt/myapp/dist/

--dry-run 不会实际修改目标端,只列出将要发生的变化。带 --delete 的同步命令尤其需要先预演——错误删除的代价远大于多花 10 秒看一眼输出。

五、文件属性:权限、时间戳、属主

传输文件不是单纯的"把数据拷过去"。权限、时间戳、属主、软链接这些属性,对服务能不能正常运行有直接影响。

属性如果不正确处理
属主/属组服务进程可能读不了配置,或者执行不了脚本
权限模式chmod +x 脚本变成不可执行;私钥权限过宽 sshd 拒绝
修改时间增量同步的比对依据,乱了以后每次都是全量
软链接丢失后变成拷贝链接目标的内容,或者变成断链

查看详细属性:

bash
ls -l deploy.sh
stat deploy.sh   # 比 ls -l 更细:精确时间、inode、权限八进制

rsync -a 尽量保留属性,但属主和属组能否保留取决于执行用户有没有权限:

bash
rsync -av ./scripts/ root@192.168.10.129:/opt/scripts/

如果不是 root 登录,同步过去的文件属主通常会变成登录用户自己。这不一定有问题,但要确认目标服务进程的用户确实能读写这些文件。

只想保留权限和时间戳、不改属主:

bash
rsync -rltD --chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r ./dist/ app@192.168.10.129:/opt/myapp/dist/

各参数拆开看:

参数含义
-r递归目录
-l保留软链接(拷贝链接本身,不是链接指向的文件)
-t保留文件修改时间
-D保留设备文件和特殊文件
--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r目标端强制设权限——目录所有人可读可进入,文件属主可写其他人只读

--chmod 的格式:D 代表目录,F 代表文件,u=属主,g=属组,o=其他人。Web 静态文件发布时这种显式权限声明比依赖源端权限更可靠。

另外一个常见问题:Windows 编辑的脚本传到 Linux 后,第一行的 #!/bin/bash 末尾带了 \r(CR),执行时报 /bin/bash^M: bad interpreter

bash
file deploy.sh       # 看是不是 CRLF
dos2unix deploy.sh   # 转换换行符

六、覆盖和删除是最危险的操作

scp 覆盖同名文件没有任何提示:

bash
scp ./nginx.conf root@192.168.10.129:/etc/nginx/nginx.conf

如果传错文件或者传错路径,覆盖之后想要还原就只能靠备份。所以覆盖配置前,先备份远端文件:

bash
ssh root@192.168.10.129 \
    'cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%F-%H%M%S).bak'

scp ./nginx.conf root@192.168.10.129:/etc/nginx/nginx.conf

cp -a 保留原文件的权限和时间戳,回滚时和原来一致。

rsync --delete 更危险——它不只是覆盖,还删除目标端多出来的文件:

bash
rsync -avP --delete ./dist/ root@192.168.10.129:/opt/myapp/dist/

这条命令的意思不是"把源端拷过去",而是"让目标端变得和源端一模一样"。目标端多出来的文件会被删掉。

可以先预演:

bash
rsync -avP --delete --dry-run ./dist/ root@192.168.10.129:/opt/myapp/dist/

另外,路径写得越具体,误删范围越小。/opt/myapp/dist//opt/myapp/ 安全得多——万一目录写错,删的是子目录,而不会把整个应用目录清掉。

rsync--delete 是根据源端来删除目标端。如果命令把源和目标写反了,或者源目录是空的(比如挂载失败),那目的地就可能被清空。这种事故偶尔还能见到——所以 --dry-run 的目的不光是确认目录层级,也是为了确认源端内容确实是你想同步的那个。

七、传完之后校验

关键文件传完之后做校验,确认两边是一致的。源端生成哈希:

bash
sha256sum app.tar.gz

远端计算:

bash
ssh root@192.168.10.129 'sha256sum /tmp/app.tar.gz'

或者把校验文件一起传过去自动比较:

bash
sha256sum app.tar.gz > app.tar.gz.sha256
scp app.tar.gz app.tar.gz.sha256 root@192.168.10.129:/tmp/

ssh root@192.168.10.129 '
    cd /tmp
    sha256sum -c app.tar.gz.sha256
'

sha256sum -c 读取校验文件,按里面的路径和哈希值比对。如果匹配,输出 app.tar.gz: OK;不匹配输出 app.tar.gz: FAILED。发布包、备份包、离线安装包,都适合加上这一步。

如果发现哈希不一致,说明文件在传输过程中损坏了——可能网络丢包、磁盘故障。TCP 有校验和但比较弱,应用层再做一次强校验(SHA-256)是对抗静默数据损坏的一个简单手段。

八、通过堡垒机传文件

有堡垒机之后,文件传输的方式取决于堡垒机的授权策略。常见方式:

方式说明
Web SFTP在浏览器里上传下载,走堡垒机审计
客户端 SFTP通过堡垒机代理连接目标资产
命令行 rsync/scp需要堡垒机或 ProxyJump 允许转发
工单附件先走审批,由堡垒机侧分发文件到目标机器

如果堡垒机只允许 Web 文件管理,自己再单独开 SCP 通道直接连目标机器,等于绕过了审计。数据离开的时候没有记录,出了事反而解释不清楚,甚至被发现后本身就是一个违规操作的证据。

通过 JumpServer 这类堡垒机时,文件传输也会进审计。授权规则里要看二件事:

检查为什么
授权是否允许文件传输有些授权只给了"连接"动作,不包含"文件上传/下载"
下载是否有审计记录带出数据的操作必须能追溯

九、lrzsz:终端里临时拖文件

lrzszrz(接收/上传)和 sz(发送/下载)这一对命令,走 ZMODEM 协议,在终端里可以直接弹出文件选择对话框。

bash
yum install lrzsz -y
apt install lrzsz -y

上传:

bash
rz           # 客户端弹出文件选择框,选中后上传到当前目录

下载:

bash
sz /tmp/debug.log   # 客户端弹出保存路径对话框

它能工作依赖终端客户端支持 ZMODEM。Xshell、SecureCRT、iTerm2 一般支持。很多 Web Terminal、Windows Terminal、VS Code 内置终端、浏览器终端不支持。

因为这个工具只在特定终端客户端上能用,而且操作过程不进入审计记录,所以只适合临时传一个很小的文件——比如排查问题时下载一段日志、上传一个临时用的脚本。正式发布包、备份包、大批量文件同步,还是要走 rsync 或堡垒机文件管理。

十、常见传输问题的排查

Permission denied

出现这个提示时,先区分是哪一层的权限问题。

SSH 认证层的拒绝:

text
Permission denied (publickey,password).

这是没连上——key 不对、用户不对、或者认证组合不对。

文件系统层的拒绝:

text
scp: dest open "/etc/myapp/app.conf": Permission denied

这个说明已经连上服务器了,但是目标用户没有写 /etc/myapp/ 目录的权限。可以先传到 /tmp,再在服务器上用 sudo 移动:

bash
scp ./app.conf ops@192.168.10.129:/tmp/app.conf

ssh ops@192.168.10.129 '
    sudo install -m 0644 -o root -g root /tmp/app.conf /etc/myapp/app.conf
'

install 命令比 mv 更适合部署单个配置文件——它可以在移动的同时设置权限、属主、属组,一步到位。

传输中断

scp 中断了只能重头传。大文件用 rsync -P

bash
rsync -avP ./bigfile.tar.gz root@192.168.10.129:/data/

-P 在中断后保留了已经接收到的文件片段,重跑时会自动续传。

文件名包含空格

本机路径加引号:

bash
scp "./app config.conf" root@192.168.10.129:/tmp/

远端路径包含空格时,转义会更麻烦。服务器上文件和目录的命名最好用连字符 - 或下划线 _ 替代空格,可以避免很多不必要的引号和转义问题。