☰
Linux文件传输工具详解:scp、ftp、wget与curl的差异与实战
2026/10/1 6:02:21 网站建设 项目流程

经常有读者在后台问我类似的问题:我要把本地的文件传到服务器,用 scp 还是 ftp?我下载一个安装包,wget 和 curl 到底有啥区别?说实话,我在刚接触 Linux 那阵也被这四个命令搞晕过——看起来都是"传文件",但实际工作方式、适用场景、底层协议完全不一样。

这篇文章就不绕弯子,直接把 scp、ftp、wget、curl 这四个工具讲透。我会从实际使用的角度出发,讲清楚它们的语法、常用参数、适用场景,以及我自己在真实环境中踩过的坑。不管你是刚入门 Linux 的新人,还是已经写了好几年 shell 脚本的老手,这篇文章都应该能帮你把这四个工具用得更顺手。

1. 为什么Linux用户迟早要面对"传文件"这件事

1.1 从一次真实的上线经历说起

我印象特别深的一次经历:刚接手一台服务器,需要把本地写好的配置文件、打包好的代码压缩包传上去部署。当时我打开终端,第一反应是用 Xshell 的拖拽上传,结果传输到一半卡住不动了;换成 rz 命令,小文件还行,一旦超过几百兆就直接失去响应。最后逼得没办法,才老老实实打开了 scp 的官方手册。

那次之后我彻底明白了一个道理:在 Linux 环境下,文件传输不是一个"能传就行"的问题,而是一个"在什么场景下、用什么协议、选哪个工具"的决策问题。你面对的是几 MB 的配置文件,还是几个 GB 的数据库备份?对方是需要交互式操作的 FTP 服务器,还是只要一条命令就能拉取文件的 HTTP 下载地址?需求不同,选型完全不同。

1.2 四个工具的定位差异:先搞清楚谁解决什么问题

很多人把 scp、ftp、wget、curl 混为一谈,默认它们都是"下载/上传文件的命令",这是最大的认知误区。我给这四个工具做个粗线条的定位:

  • scp是"安全复制",底层走的是 SSH 协议。它适合在你自己有账号权限的服务器之间传文件,不需要额外搭建任何服务端,只要目标机器开着 SSH 就行。
  • ftp是"文件传输协议"里的正统军。它需要一台运行 FTP 服务端的机器,适合做目录浏览、批量上传下载、多用户权限管理,但默认是明文传输。
  • wget是一个"下载器"。它针对的是 HTTP/HTTPS/FTP 的下载场景,比如从软件源拉一个安装包、镜像整个网站目录,它擅长递归、续传、后台下载这些东西。
  • curl是一个"传输工具",它的能力边界比 wget 大得多。不只是下载,还能发 POST 请求、带上请求头、模拟表单提交、上传文件、调试接口。很多后端同学把它当 API 测试工具用。

先记住这个分工,然后我们再逐个展开。你在实际使用中遇到的很多困惑,其实都是"用 A 工具干 B 工具该干的活"造成的。

2. scp:基于SSH的安全文件复制,服务器间传输的最稳选择

2.1 scp基本语法与三种最常见用法

scp 的全称是 secure copy,它把文件复制建立在 SSH 加密通道之上,所以安全性是天然的。它的基本语法和 cp 非常像,只是多了"远程主机"这一维度:

scp [选项] 源路径 目标路径

路径的写法规则是:如果目标是远程机器,就写成用户名@主机地址:路径。我实际工作中最常用的就三种:

第一种:本地推送到远程服务器

scp ./app.tar.gz root@192.168.1.100:/opt/deploy/

这条命令把当前目录下的 app.tar.gz 复制到 192.168.1.100 这台机器的 /opt/deploy/ 目录下。执行后会提示输入 root 的 SSH 密码(前提是你没有配置免密登录)。

第二种:从远程服务器拉取到本地

scp root@192.168.1.100:/var/log/nginx/access.log ./logs/

这个方向是反向操作,把远程机器的日志文件拉回本地分析。我排查线上问题的时候经常用这一条。

第三种:两台远程服务器之间直接传输

scp root@192.168.1.100:/data/backup.sql root@192.168.1.101:/data/

注意这种写法有个细节:数据是先经过你本机中转的,不是两台服务器直接对接。如果两个服务器之间网络延迟很高,中间再经过你本机,速度会明显变慢。这时候更专业的做法是登录到其中一台服务器上再执行 scp,让两台服务器之间直连。

2.2 常用参数与免密配置

scp 的参数不多,但有几个必须记牢:

参数作用备注
-P指定 SSH 端口大写 P,因为小写 p 是保留时间戳
-r递归复制整个目录复制目录必带
-C传输时启用压缩传文本文件效果好,传已压缩的 tar.gz 反而增加 CPU 开销
-i指定私钥文件多密钥环境必备
-l限制带宽单位是 Kbit/s,注意不是 KB/s
-p保留文件的修改时间和访问时间小写,和 -P 含义完全不同

我特别想提醒-P和-p的区别。有一次同事问我为什么 scp 指定端口一直报错,我一看他写的是scp -p 2222 file user@host:/path,这就是把-p当成端口参数了,结果它把"保留时间戳"这个功能打开了,端口反而用的默认 22。这个大小写问题,坑了无数人。

如果你的服务器 SSH 端口改成了 2222,那么命令要写成:

scp -P 2222 ./file.tar.gz root@192.168.1.100:/opt/

实际工作中,频繁输入密码很影响效率,所以配置免密登录是标准操作。做法分两步:第一步在本地生成密钥对,第二步把公钥追加到远程机器的 authorized_keys 文件里。

ssh-keygen -t ed25519 -C "your_email@example.com" ssh-copy-id root@192.168.1.100

ssh-keygen生成密钥时一路回车即可,默认生成到 ~/.ssh/ 目录下。ssh-copy-id是专门用来把公钥推送到远程的工具,它会在远程创建 ~/.ssh/authorized_keys 文件并追加公钥,整个过程自动化完成。配置好之后,scp 和 ssh 都不需要再输密码了。

2.3 scp的短板:为什么它不适合做持续同步

scp 虽然好用,但它有一个硬伤:不支持断点续传,也没有增量同步的能力。假如你传一个 5GB 的文件,传到 80% 的时候网络断了,重新执行 scp 就得从头再来一遍。

另外,scp 每次都是"全量复制",不管目标路径下是否已经有相同的文件。你在本机改了一个 10MB 文件中的 1KB,scp 也会把整个 10MB 都传过去。对于频繁更新、大量小文件的场景,这个效率是很低的。

所以我的个人习惯是:一次性传送、服务器数量少、文件体积适中的场景用 scp;如果是持续备份、目录同步这种需要增量传输的场景,直接上 rsync。rsync 支持断点续传、增量同步、压缩传输,还能通过 SSH 通道加密,几乎是 scp 的完美替代品。scp 适合"传一次就完事",rsync 适合"要长期保持两边目录一致"。

3. ftp:老牌协议在批量管理场景下依然能打

3.1 从客户端连接说起:ftp与lftp

很多人觉得 FTP 已经过时了,其实在批量文件管理这个细分领域,FTP 的生态依然非常成熟。比如企业内部的文件服务器、部分厂商设备的固件升级、网站旧版后台的文件管理,很多还是基于 FTP。

Linux 自带的 ftp 命令是一个最基础的交互式客户端,连上服务器之后,你会进入一个 ftp> 提示符:

ftp 192.168.1.200

连接成功后输入用户名密码,就能用ls、cd、get、put这些命令操作了。get是下载单个文件,put是上传单个文件。

但 ftp 这个命令有个让人难受的地方:不支持批量下载的通配符操作。你想下载当前目录下所有 .tar.gz 文件,用mget *.tar.gz倒是可以,但它会逐个文件询问你确认(除非你先输入prompt off关闭交互确认)。用起来总觉得不够痛快。

所以我平时在命令行里操作 FTP,更推荐用lftp。它是一个功能强大得多的 FTP/HTTP 客户端,最吸引我的两点:一是支持mget、mput批量操作且默认不啰嗦,二是支持mirror目录镜像。看几个例子:

lftp -u username,password 192.168.1.200

登录后输入:

mget *.tar.gz # 批量下载当前目录所有 tar.gz mput *.log # 批量上传本地所有 log 文件 mirror -c /remote/path /local/path # 镜像远程目录到本地,支持断点续传

mirror这个命令是我用 lftp 最大的原因。它可以把 FTP 服务器上的整个目录结构一次性同步到本地,中途断了再执行还能续传,等于是在 FTP 协议之上实现了一个简化版的 rsync 效果。

3.2 主动模式与被动模式:90%的FTP连不上都卡在这

不管用哪个 FTP 客户端,你迟早会碰到一个经典问题:能登录成功,执行ls也正常,但一执行下载就卡住,最后超时。这个问题十有八九出在主动模式和被动模式的选择上。

FTP 协议和 HTTP 不一样,它需要两条连接:一条是控制连接(默认 21 端口),另一条是数据连接。主动模式下,服务器主动去连接客户端的某个端口来传数据;被动模式下,服务器开放一个随机高端口,由客户端主动去连。

放在实际网络环境里:如果客户端在 NAT 后面(比如你本地电脑通过路由器上网),主动模式基本是废的,因为服务器没法主动穿透你的 NAT 去连你的内网端口。这时候必须切到被动模式。lftp 里可以这样设置:

set ftp:passive-mode on

vsftpd 服务器端默认是开启被动模式的,但很多云服务器厂商的安全组策略只放行了 21 端口,导致被动模式的数据连接被防火墙拦死。这种情况需要在服务器端配置被动端口范围,然后在防火墙和安全组里一并放行:

# /etc/vsftpd/vsftpd.conf pasv_enable=YES pasv_min_port=30000 pasv_max_port=31000

然后在防火墙放行:

firewall-cmd --permanent --add-port=30000-31000/tcp firewall-cmd --reload

每次排查 FTP 连接问题,我都会按这个顺序走一遍:先看控制连接通不通,确认被动模式是否开启,再检查数据端口是否被防火墙拦截。90% 的问题都能解决。

3.3 用vsftpd快速搭一个本地测试服务

如果你想自己动手验证 FTP 的各种操作,一条命令在 Ubuntu/Debian 上就能装好 vsftpd:

apt update && apt install -y vsftpd

装完后修改 /etc/vsftpd.conf,把匿名访问关掉,允许本地用户登录:

anonymous_enable=NO local_enable=YES write_enable=YES

重启服务:

systemctl restart vsftpd

这样你本机的系统用户就能通过 FTP 登录了。但要注意,出于安全考虑,默认配置下用户登录后只能访问自己的家目录,这是 vsftpd 的 chroot 机制在起作用。如果你发现登录后看不到别的目录,不用慌,那是安全特性,不是故障。

说实话,搭建 FTP 服务端在当前这个强调安全的环境里不算首选。如果只是临时给同事共享个文件,我更推荐用 Python 一行命令起一个 HTTP 服务;但如果你确实需要多用户、多目录、细粒度权限的共享方案,FTP 依然是一个成熟可靠的选项。

4. wget:下载场景的瑞士军刀,镜像站点的首选利器

4.1 从单文件下载到批量下载

wget 是 Linux 上最老牌的下载工具(GNU 项目出品),它的核心定位就是"从网络上下载资源"。最简单的用法:

wget https://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-2009.iso

默认情况下,文件会下载到当前目录,文件名保持服务器上的原始名称。

但实际使用中,我几乎不会用默认方式,因为很多时候你不希望文件名被服务器那边随便定。这时候用-O参数指定输出文件名:

wget -O mylinux.iso https://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-2009.iso

-O还有另一个妙用:配合-q(安静模式),可以静默下载并把内容输出到标准输出,供管道使用:

wget -qO- https://example.com/version.txt | grep "VERSION="

这在写自动化脚本时非常实用。比如启动一个服务前,先动态获取最新版本的配置文件,直接通过管道处理后写进目标路径,全程不用落地临时文件。

如果遇到一个网页里有大量附件需要批量下载,可以先把所有链接提取出来,配合 xargs 并行下载:

grep -o 'https://example.com/files/[^"]*' page.html | xargs -P 4 -I {} wget {}

这里-P 4表示同时跑 4 个 wget 进程,能明显提升下载速度,但要注意别把对方服务器压垮。

4.2 断点续传和限速:下载大文件的保命技能

下载大文件最怕什么?怕下载到 50% 网络断了。重来一遍不仅浪费时间,还可能因为对方服务器限速或者连接数限制导致永远下不完。wget 的-c参数就是为了这个场景准备的:

wget -c https://mirrors.aliyun.com/ubuntu-releases/22.04/ubuntu-22.04.4-desktop-amd64.iso

-c表示 continue,从上次中断的位置继续下载。这个功能实测非常可靠,前提是服务器端支持断点续传(HTTP 协议里对应 Range 请求头,大部分下载站都支持)。

另一个容易忽略的参数是--limit-rate,限速下载:

wget --limit-rate=500k https://example.com/largefile.zip

这个参数用的场景比较微妙。比如你在生产服务器上下载一个大文件,但不希望下载占满所有带宽,影响线上服务的正常响应,限速就非常有用。我可以明确说一下,这里的单位是字节/秒,500k就是 512000 字节每秒。

还有后台下载:

wget -b https://example.com/bigfile.iso

命令加-b之后,wget 会退到后台执行,并把日志写到当前目录的 wget-log 文件里。你可以随时用tail -f wget-log查看下载进度。

4.3 递归与镜像:一键拉取整个站点的背后逻辑

wget 最"惊艳"的能力是递归下载,它可以把一个网站页面里引用的所有资源(图片、CSS、JS、内部链接)都抓下来,实现"离线浏览"甚至整站镜像的效果。

wget -r -l 3 -np https://docs.example.com/

解释一下这几个参数:

  • -r:开启递归下载。
  • -l 3:限制递归深度为 3 层,防止无限深入。
  • -np:不上升到父目录,避免把整个域名下的所有内容都拉下来。

如果想做完整的镜像,还有一个更省事的-m参数,它等价于"递归 + 无限深度 + 保留时间戳 + 不创建空目录"的组合:

wget -m https://example.com/

但要提醒一句,镜像网站前务必确认你对目标站点有合法访问权限,同时注意控制递归深度。我就见过有人递归下载一个技术文档站,因为某个页面上有个"返回首页"的链接,wget 顺着这个链接把全站几十万张图片全抓下来了,硬盘直接塞满。

如果某些文件你明确不需要,可以用-R排除:

wget -r -np -R "*.pdf,*.zip" https://docs.example.com/

这种精细控制能力,是 wget 在下载工具中不可替代的原因。

5. curl:不只是下载工具,更是API调试与协议测试利器

5.1 curl与wget的本质区别

curl 和 wget 在"下载文件"这件事上功能重叠,但它们的核心设计理念完全不同。wget 的定位是"下载器",它关心的是怎么把资源完整地搞下来;curl 的定位是"传输工具",它支持几十种协议(HTTP、HTTPS、FTP、SFTP、SMTP、IMAP 等),更关心的是"怎么和协议交互"。

所以你在用 curl 时,它默认把响应的内容输出到终端(标准输出),而不是直接存成文件。这种设计看似"不友好",实则是它最大的优势:你可以直接看到返回内容,配合管道做各种处理,比如测试一个 API 接口:

curl https://api.example.com/users/123

这条命令会直接打印出接口返回的 JSON 数据,不需要落地成文件再去读。

5.2 从GET到POST:用curl完成一次接口调用

我在日常工作中用 curl 最多的场景,是调试 RESTful API。一个完整的 POST 请求通常长这样:

curl -X POST https://api.example.com/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

逐段拆解:

  • -X POST:指定请求方法。不过大多数情况下可以不写,因为-d参数会自动把请求变成 POST。
  • -H:添加请求头。这里声明了请求体是 JSON 格式。
  • -d:发送请求体数据。

如果接口需要携带 Token,那就再加一个请求头:

curl -X GET https://api.example.com/user/info \ -H "Authorization: Bearer eyJhbGciOi..."

curl 还支持文件上传,用-F模拟表单提交:

curl -X POST https://api.example.com/upload \ -F "file=@/home/user/test.txt"

这里的@符号后面跟本地文件路径,非常直观。

5.3 证书、重定向与上传:curl绕不开的几个细节

刚接触 curl 的人,一般会碰到这几个问题。

第一个问题:访问 HTTPS 站点报证书错误。错误信息通常是curl: (60) SSL certificate problem。原因五花八门:可能是服务器证书过期了,可能是自签名证书,也可能是本地 CA 证书库太旧。如果你确定对方站点是可信的,可以临时加上-k参数跳过证书校验:

curl -k https://self-signed.example.com/

但我要强调:-k是排查问题时的临时手段,不适合作为长期方案。跳过证书校验意味着中间人可以伪造服务器身份,通信内容可以被窃取或篡改。更稳妥的做法是下载对方站点的 CA 证书,然后用--cacert指定:

curl --cacert /path/to/ca.crt https://self-signed.example.com/

第二个问题:访问的 URL 发生了 301/302 跳转。有时候你请求的地址会自动跳转到另一个地址(比如 http 跳转到 https),curl 默认不会自动跟随跳转,而是把 301 响应本身返回给你。要跟随跳转,加-L参数:

curl -L https://example.com/download

第三个问题:把 curl 的输出接到流程里。前面说了,curl 默认把内容打到标准输出,所以它天然适合放进管道里。一个很经典的场景是下载并执行安装脚本:

curl -fsSL https://example.com/install.sh | bash

这里的-f表示遇到 HTTP 错误(比如 404)时静默失败,不要输出错误页面;-s安静模式,不显示进度条;-S配合-s时,出错时依然显示错误信息;-L跟随重定向。直接把脚本通过管道交给 bash 执行,一步到位。用这种方式时要对远程脚本的来源有信心,最好先下载下来看一眼内容再执行。

第四个问题:接口响应太慢,卡死在那里。给 curl 加上超时限制是基本素养:

curl --connect-timeout 5 --max-time 10 https://api.example.com

--connect-timeout限制的是建立连接的时间,--max-time限制整个请求的总时长。在脚本里调试 HTTP 接口,这两个参数几乎是必加的,能有效避免脚本因为网络问题而无限挂起。

6. 四个工具放在一起,到底怎么选

6.1 一张表看清边界

我把这四个工具的关键属性放在一张表里,方便对比和查阅:

工具底层协议典型场景是否加密断点续传交互式操作需要服务端
scpSSH服务器间文件复制、目录推送/拉取是否否仅需 SSH 服务
ftp / lftpFTP批量上传下载、多用户文件共享否(FTPS 可加密)部分支持是需 FTP 服务端
wgetHTTP/HTTPS/FTP下载安装包、镜像网站、递归抓取是(HTTPS)是否否
curl几十种协议API 调试、表单提交、文件上传下载、协议测试是(HTTPS)是(针对下载)否否

注意 ftp 这一行,默认的 FTP 协议是明文传输,如果传的是敏感数据,建议换成 FTPS(FTP over SSL)或者直接用 SFTP。SFTP 也是基于 SSH 的,和 scp 同源,但交互能力更强,支持断点续传和目录浏览。多说一句,很多人把 SFTP 和 FTPS 搞混:SFTP 是 SSH 协议家族的一部分,FTPS 是 FTP 协议加了一层 SSL 加密,二者完全不同。

6.2 按实际场景的选型建议

基于我自己的使用经验,遇到具体需求时可以参考下面这套思路:

  • 要把本地文件放到自己管理的服务器上:首选 scp。简单直接,只要目标机器能 SSH 登录就能用。如果文件特别大或者需要频繁同步,换成 rsync。
  • 要和一台公共文件服务器交互,对方明确给了 FTP 地址和账号:用 lftp。它的 mirror 和批量操作能力能节省大量时间。
  • 需要从一个 URL 下载资源,比如软件安装包、ISO 镜像、静态文件:用 wget 或 curl 都行。我个人的习惯是,简单下载用 wget,因为参数更贴合下载场景;需要看响应内容、调试接口或者上传文件时用 curl。
  • 需要测试后端接口、模拟 HTTP 请求、排查网络问题:直接用 curl。它是这四个工具里唯一能精细控制请求方法、请求头、请求体的工具。

有一个很常见的误区是:很多人坚信 curl 能做的 wget 都能做,或者反过来。实际上,就算在"下载"这个交集场景里,两者也有明显差异。wget 的递归下载能力是 curl 不具备的(curl 需要配合其他工具才能实现递归);而 curl 对协议的控制粒度、对请求头和请求体的精细操作,wget 也做不到。所以不是"谁替代谁"的关系,而是各管一摊。

7. 实战中踩过的坑与排查经验

7.1 scp提示host key验证失败

有一次我重装了一台测试服务器的系统,然后用 scp 连它,结果报错:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

这是因为本机的 known_hosts 文件里还存着这台服务器的旧 SSH 指纹,重装系统后指纹变了,SSH 客户端为了防中间人攻击,直接拒绝连接。解决办法不是把公钥删了重来,而是先确认这台服务器确实是你熟悉的那台(排除 DNS 劫持或网络中间人可能性),然后移除旧指纹:

ssh-keygen -R 192.168.1.100

之后再重新连接,它会提示确认新的指纹,输入 yes 就恢复正常了。这个命令很多新手不知道,遇到指纹冲突就以为系统坏了,其实一行ssh-keygen -R就解决。

7.2 FTP文件名乱码

从 Windows 那边的 FTP 服务器下载文件名带中文的文件,经常出现乱码。原因在于 Windows 的 FTP 服务器默认用 GBK 编码传输文件名,而 Linux 客户端默认按 UTF-8 解析。lftp 里可以设置远程编码:

set ftp:charset "GBK" set file:charset "UTF-8"

这两行的意思是:远程 FTP 服务端用 GBK 编码,本地文件系统用 UTF-8。设置后再执行ls和mget,文件名就正常了。这个问题特别隐蔽,卡了我将近一个小时才搞明白,分享出来希望看到的人少走弯路。

7.3 wget递归下载导致的死循环

有一回我想把一个在线文档站点拉下来离线阅读,执行了wget -r -np https://docs.example.com/,结果下载了一堆没用的页面——因为文档里引用了大量外部 CDN 资源,而某些页面里又包含"编辑本页"这类指向外站的链接,wget 顺着链接越抓越远。

正确的做法是加-A参数限定只下载特定类型文件,比如只下载 HTML 和图片:

wget -r -np -A "*.html,*.jpg,*.png,*.css,*.js" https://docs.example.com/

另外,--reject-regex可以根据正则排除外站链接,例如:

wget -r -np --reject-regex "(edit|login|logout)" https://docs.example.com/

在我后来长期维护离线文档的经验里,递归下载之前先花两分钟理清链接关系,整站镜像的成功率会高很多。

7.4 curl的SSL证书报错

我帮同事排查过一个有意思的问题:服务器上 curl 访问某个接口一直报curl: (60) SSL certificate problem: unable to get local issuer certificate,但浏览器访问同样的网址是正常的。

这个问题的根源是服务器的 CA 证书库太旧,缺少了信任该接口证书所需的根证书。解决方案不是甩一个-k上去,而是更新系统的 CA 证书:

apt install --reinstall ca-certificates update-ca-certificates

执行后再用 curl 访问就正常了。这个坑提醒我一件事:遇到证书错误,先别急着跳过校验,先查一下是不是本地信任库本身的问题。-k是最后手段,不是首选方案。

还有一个相关的小技巧:排查 HTTPS 接口问题的时候,用curl -v可以打印完整的握手过程、请求头和响应头,信息量远大于默认输出。加上-v之后,你能看到 curl 使用了哪个 CA 证书、TLS 版本号、协商的加密套件,定位问题会高效很多。

这四个工具,我几乎每天都在用。scp 是我部署服务器的"手",wget 是我拉取资源的"嘴",curl 是我调试接口的"眼",lftp 则是我处理批量文件时的"仓库管理员"。熟练使用它们并不难,难的是在合适的场景选出合适的工具。希望这篇内容能让你少走一些我当初走过的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询