有两款命令行工具,它们都通过 HTTP 获取内容,几乎安装在每台机器上,却总被无休止地比较,却很少有人能有意义地区分它们。
令人欣慰的是,关于两者区别的最可靠来源是 curl 维护者自己发布的对比文档 —— 该文档中专门有一节详细说明了 wget 在哪些方面比 curl 更胜一筹。 下文中的每项事实陈述均可追溯至该文档或各项目的官方手册,而非基于任何人的主观印象——这一点在如此频繁凭记忆撰写的对比中尤为重要。
我们是 Geonode,我们销售代理服务,而诚实的说明很简短:对于绝大多数使用场景而言,这两款工具都不需要代理。 获取文件、调用 API、检查服务是否响应——这些操作都不需要代理。 两者在代理支持方面确实存在一个鲜少被提及的差异,下文会有专门章节说明,但如果你是来这里挑选下载工具的,大可忽略这一部分。
有用的总结是:这两款工具是基于不同的思维模型构建的,几乎所有表面上的差异都源于此。curl 的行为类似于 cat——它获取内容并将其写入标准输出。wget 的行为类似于 cp——它获取内容并将其写入文件。 这是维护者本人提出的框架,一旦理解了这一点,重定向的默认行为、参数差异以及递归能力就不再显得随意了。
简短的建议(供希望在了解细节前先掌握要点的人参考):当需要将文件保存到磁盘时使用 wget,其他情况均使用 curl。
一句之差
用 curl 维护者的话来说,curl 的工作方式“类似于传统的 Unix cat
命令”。而 wget 的工作方式“更像 cp
”。
这一细微差别解释了下文的大部分内容。
实际应用中的含义
curl https://example.com/file.txt # prints the contents to your terminal
wget https://example.com/file.txt # saves file.txt to the current directory
两者都没有错。它们只是在回答不同的问题。
curl 的设计假设你想要的是数据,而数据后续的处理方式由你决定——无论是通过管道传输、解析、重定向还是读取。 写入标准输出是可组合的选择,这也是 curl 能自然融入 shell 管道的原因。
wget 的设计假设你需要的是文件。它会选择文件名、创建文件、显示进度条,并最终在磁盘上生成文件。
为何影响比表面看起来更大
一旦工具的任务是“将文件存入磁盘”,一系列行为就变得理所当然:跟随重定向,因为文件已经移动;失败时重试,因为目标是文件本身而非尝试过程;恢复未完成的下载,因为只下载了一半的文件并不算完成任务。wget 默认会执行所有这些操作。
一旦工具的任务是“执行此传输并返回结果”,正确的行为就不同了:报告发生了什么,而不是替我做决定;完全按照要求执行;并让调用者处理其余部分。curl 会报告重定向并停止,因为跟随重定向并非你的要求。
**这两种默认行为都没有优劣之分。**它们与各自不同的设计目的相一致,而人们对这两种工具感到沮丧的原因,大多在于期望对方遵循自己所用工具的假设。
curl 能做到而 wget 做不到的
根据维护者的对比,这是一份相当详尽的列表。
它首先是一个库
curl 附带了 libcurl,据描述它拥有“一个稳定且人人皆可使用的 API”。 这是最关键的区别,也是在终端中最为隐蔽的一点。
libcurl 被嵌入到海量的软件中——包括语言绑定、应用程序和设备。从某种意义上说,命令行工具只是该库的演示。wget 是一个程序;而 curl 是一个构建在库之上的程序,其他程序会调用该库。
如果你曾经使用过 PHP 的 cURL 函数,或者基于 libcurl 构建的某种语言的 HTTP 绑定,那么你其实是在未直接运行 curl 的情况下使用了 curl。
支持的协议远不止于此
已公布的列表很长:“FTP(S)、GOPHER(S)、HTTP(S)、SCP、SFTP、TFTP、TELNET、DICT、LDAP(S)、 MQTT、FILE、POP3(S)、IMAP(S)、SMB(S)、SMTP(S)、RTMP、RTSP 和 WS(S)”。
wget 支持 HTTP、HTTPS 和 FTP。对于 Web 应用而言,这通常已经足够。 对于涉及邮件协议、SFTP、MQTT 或 WebSocket 的任何情况,curl 是本文讨论的两个工具中唯一能胜任的。
较新的 HTTP 版本
curl 支持 HTTP 0.9、1.0、1.1、2 和 3。 如果你需要专门测试服务器在 HTTP/2 或 HTTP/3 下的行为表现,那就该用 curl 了。
更多代理类型
curl 支持 HTTPS 代理以及 SOCKS4 和 SOCKS5 代理。 这是 curl 具备而 wget 不具备的功能之一,且具有实际意义——请参阅下文的代理部分。
并行传输
curl 可以通过 -Z 同时运行多个传输任务。在获取大量小型资源时,若逐个处理的延迟占主导地位,此功能便十分有用。
双向传输与表单上传
不仅能接收数据,还能发送数据。支持多部分表单上传、PUT 请求以及任意方法。wget 本质上是一个检索工具;而 curl 是一个传输工具,且上传是其核心功能之一。
它已在更多机器上预装
macOS 以及 Windows 10 和 11 系统中均预装了 curl。在未安装任何额外软件的 Windows 机器上,curl 可用而 wget 通常不可用——对于跨平台脚本而言,这一点的重要性远超预期。
wget 能做到而 curl 做不到的
这同样出自 curl 维护者之手,这也正是这份清单值得信赖的原因。
递归下载
“与 curl 相比,wget 的主要优势在于其递归下载能力。”
这是最重要的优势,绝非微不足道。wget 可以追踪页面中的链接并下载所找到的内容,支持指定深度,同时会将链接转换为本地浏览格式。只需一条命令,即可镜像一个文档网站以便离线阅读。
wget -r -np -k -p https://example.com/docs/
递归下载、无需指定父目录、将链接转换为本地浏览格式、获取页面必需资源(如图片和样式表)。
curl 完全无法做到这一点。 curl 仅能获取你提供的 URL。它不解析 HTML、不发现链接,也没有“网站”的概念。 如果你的任务是“复制网站的这一部分”,wget 就是答案,而 curl 没有任何值得一试的等效功能。
恢复中断的下载
wget “能够从过早中断的下载中恢复并继续下载”。使用 -c 参数,中断的下载将从中断处继续。
curl 也可以通过 -C - 实现这一点,但 wget 在重试和恢复方面的行为更自动化、更容错——当传输量大且连接不稳定时,这一点尤为重要。
常见情况无需任何选项
wget 无需任何参数即可下载文件。而 curl “需要 -o 或 -O” 才能将文件写入文件而非终端。
对于用户使用这两款工具时最常见的任务——下载文件——wget 的命令更简短,而对于日常使用的工具而言,命令简短确实是一大优势。
更合理的下载默认设置
wget“默认启用了更多功能:Cookie、跟随重定向、时间戳”。
时间戳功能特别值得注意:使用 -N 时,wget 仅在远程版本更新时才会重新下载文件。 对于一组文件的定时同步,这正是理想的行为,而 curl 没有直接对应的实现。
许可证
wget 采用 GPL v3 许可证;curl 采用 MIT 许可证。如果您将其中任何一个嵌入到产品中,这一差异可能比本页上的任何功能都更为重要。
让你浪费时间的默认设置
这些差异最可能让你整个下午都摸不着头脑。
重定向
wget 默认会跟随重定向,而 curl 不会。
这是导致“为什么 curl 没有返回任何内容”这一问题的最常见原因。服务器返回了 301 状态码,curl 报告了该状态并停止执行,标准输出看起来是空的。
curl -L https://example.com/moved # follow them
wget https://example.com/moved # already following them
这并非 curl 的疏漏。跟随重定向意味着向未指定名称的主机发送你未请求的请求,而 curl 的设计理念是执行指令并报告其余情况;wget 的设计理念则是获取文件,而该文件已被移动。
输出目标
上文已提及,但值得再次强调,因为这经常让用户感到困惑。curl URL 用于打印;wget URL 用于保存。
错误处理
两者都有一个特殊之处。curl 将 404 视为传输成功并以 0 退出——传输成功了,服务器已响应。使用 -f 可使 HTTP 错误产生非零退出状态。
wget 在发生 HTTP 错误时默认返回非零退出状态,这对下载工具而言是更直观的行为。
在脚本中:curl -sSf 这一组合值得记住,而遗漏它往往是导致故障任务看似正常运行的常见原因。
重试
wget 默认会重试。curl 则不会,除非你通过 --retry 显式要求。
这再次与模型一致:下载工具应坚持尝试,传输工具应报告状态。
实用建议
如果你正在编写任何自动化脚本,请明确设置行为,而不是依赖任一工具的默认设置。curl -sSfL --max-time 30 说明了你的需求。wget --tries=3 --timeout=30 也是如此。明确的命令即使在一年后被他人阅读,也能保持清晰。
并排对比
| 任务 | curl | wget |
|---|
| 输出到终端 | curl URL | wget -O - URL |
| 保存到文件 | curl -O URL | wget URL |
| 以指定名称保存 | curl -o name URL | wget -O name URL |
| 跟随重定向 | curl -L URL | 默认 |
| 恢复下载 | curl -C - -O URL | wget -c URL |
| 仅下载头部信息 | curl -I URL | wget --spider -S URL |
| 自定义头部 | curl -H "K: V" URL | wget --header="K: V" URL |
| 基本认证 | curl -u user:pass URL | wget --user=u --password=p URL |
| POST 数据 | curl -d "a=b" URL | wget --post-data="a=b" URL |
| 静默模式 | curl -s URL | wget -q URL |
| 镜像网站 | 不可用 | wget -m URL |
| 上传文件 | curl -T file URL | 不可行 |
| 使用 SOCKS5 | curl -x socks5h://host URL | 不原生支持 |
| 并行传输 | curl -Z ... | 不支持 |
表格解读
这种对称性同样适用于日常操作——大多数任务都有直接对应的等效操作,只是参数写法不同,这些差异虽令人困扰但并不重要。
标注为“不可行”的四行才是真正需要做出选择的地方。 网站镜像仅支持 wget。上传仅支持 curl。SOCKS 代理仅支持 curl。并行传输仅支持 curl。
如果您的任务属于上述行之一,那么比较就到此为止,您可以停止阅读了。如果不属于,两种工具均可使用,您应选择自己更熟练的那一种。
关于参数冲突的说明
在两种工具中,-O 表示不同的含义,这确实是一个陷阱。
在 curl 中,-O 表示“使用 URL 中的文件名保存”,而 -o name 表示“保存为该名称”。在 wget 中,-O name 表示“保存为该名称”,而无需使用另一个选项。
因此,curl -O 和 wget -O 并非等价,若在两者之间粗心转换命令,将会导致意想不到的结果。
根据任务选择使用哪种工具
这是一份决策指南,而非定论。
何时使用 wget
当你需要将文件下载到磁盘时。 无需参数,有进度条,默认设置合理。这对大多数人来说是主要情况。
需要镜像下载或递归下载时。 这是唯一的选择。使用 wget -m 或 wget -r 并设置适当的限制。
网络连接不稳定且文件较大时。 支持自动重试和 -c 断点续传。
需要保持本地副本与源同步时。 -N 通过时间戳机制,仅下载已更改的部分。
您希望使用尽可能简短的命令,以便在他人将要阅读的脚本中进行直接下载。
何时使用 curl
您正在使用 API。 涉及请求头、方法、请求正文以及通过管道传输到 JSON 处理器的输出。
你需要发送数据,而不仅仅是检索数据。
你在进行调试。 -v 和 -w 会显示实际发送的请求、响应以及按阶段细分的耗时。这是 curl 在日常使用中最突出的优势,其他工具难以企及。
你需要支持 HTTP 和 FTP 以外的协议。
你需要 SOCKS 或 HTTPS 代理支持。
你在 Windows 或 macOS 上且未安装任何软件,此时 curl 通常已预装,而 wget 通常没有。
你正在编写将作为代码使用的程序,因为 libcurl 的绑定使得命令结构能够自然转换。
两者兼用
对于大多数实际工作环境而言,这是最诚实的答案。它们体积小、免费,且各有所长。同时安装两者并根据需要选择使用,并非优柔寡断——而是因为它们确实是截然不同的工具,这种做法恰恰是正确的。
两种情况下的代理
这属于我们的领域,其中有一个值得了解的实质性区别。
curl
# HTTP proxy
curl -x http://proxy.example.com:8080 https://example.com
# With credentials
curl -x http://user:pass@proxy.example.com:8080 https://example.com
# SOCKS5, with the proxy resolving hostnames
curl -x socks5h://proxy.example.com:1080 https://example.com
curl 支持 HTTP 代理、HTTPS 代理以及 SOCKS4/SOCKS5
,所有这些都通过带有协议的 -x
选项实现。
wget
# Via environment variables
export http_proxy="http://proxy.example.com:8080"
export https_proxy="http://proxy.example.com:8080"
wget https://example.com
# With credentials
wget --proxy-user=user --proxy-password=pass https://example.com
wget 会读取标准的代理环境变量,并提供代理凭据相关选项。
关键差异
wget 不原生支持 SOCKS。 curl 的功能对比列表中明确指出,SOCKS4 和 SOCKS5
属于 curl 支持而 wget 不支持的功能。
如果您的代理仅支持 SOCKS,wget 无法直接使用它。常见的解决方法是通过 proxychains 等工具运行 wget,或者在其前端部署一个本地 HTTP 到 SOCKS 的桥接器——这两种方法虽然都能奏效,但都会增加一个可能在无声中发生故障的组件。
如果你在两者之间做选择,而你的环境中已有 SOCKS 配置,那么这便成了决定性因素。
再次说明 DNS 的细节
这一点值得重申,因为它适用于任何使用 curl 配合 SOCKS 的场景。socks5://
会在你的机器上解析主机名;socks5h://
则将主机名发送给代理。 前者会将你访问的每个主机名泄露给本地解析器——即使流量被正确路由——并且对于基础设施依赖地理位置的网站,可能会返回错误的区域地址。
除非你有特别的理由不这样做,否则请使用 socks5h://
。
以及我们自己说服自己放弃销售的那部分
如果您正在下载文件、调用已拥有凭据的 API,或检查服务是否正常运行,则完全不需要代理。代理在这些工具中的价值在于地理位置验证,以及受单个地址速率限制约束的大量数据处理任务。对于其他所有情况,代理只会增加延迟、引入故障点并产生费用。
当两者都不是合适工具时
在某些常见情况下,需要其他解决方案,而使用这两者中的任何一种都会白白浪费一个下午的时间。
页面由 JavaScript 渲染。 这两种工具都会获取服务器发送的内容。如果内容随后在浏览器中组装,你得到的将是一个空壳,而任何 flag 都无法解决这个问题。 你需要一个无头浏览器——Playwright、Puppeteer 或类似工具。
你需要与页面进行交互。 点击、滚动、填写表单、等待内容出现。答案同样如此。
你正在构建一个可通过代码维护的系统。 从应用程序中调用 curl 是一种常见的捷径,但这种做法很快就会过时。请使用你所用编程语言的 HTTP 库,或 libcurl 的绑定,并确保进行正确的错误处理。
你需要双向同步一个目录。 rsync 是合适的工具,它在这方面的表现远胜于递归 wget。
你在你控制的服务器之间传输数据。 scp、rsync 或 sftp —— 这些工具专为该用途设计,速度更快,并且能正确处理权限和部分传输。
你需要检查或修改传输中的流量。 拦截代理工具才是正确的选择。
您正在从视频平台下载媒体文件。 专用工具能够处理清单解析和流组装,而上述方法均无法做到这一点。
数据通过其他方式提供。 例如 API、批量下载、公开数据集或 RSS 源。验证过程需耗时十分钟,且往往在项目尚未开始前就已终止。
关于递归下载的注意事项
关于 wget -r 有一项特别的警告:该命令功能强大,但很容易误操作到你未预期的目标。
若未设置 -np,它会向上遍历父目录;若未设置 --level,它会向下遍历很深。 如果不使用 --wait,它将以服务器响应的最快速度进行请求,这对他人的基础设施来说是一种不礼貌的行为,也是被封禁的绝佳方式。
至少应使用:wget -r -np --level=3 --wait=1 URL。并且请先检查 robots.txt 以及该网站的条款——wget 默认会遵守 robots.txt,禁用该功能是一种决策,而非单纯的便利。
大家还问
curl 和 wget 有什么区别?
curl 的工作原理类似于 cat —— 它会获取数据并将其写入标准输出。wget 的工作原理类似于 cp —— 它会获取数据并保存为文件。curl 支持更多的协议、上传、SOCKS 代理和并行传输;而 wget 能够进行递归下载和网站镜像,这些功能 curl 完全不具备。
curl 比 wget 更好吗?
两者没有优劣之分。curl 是一个基于库的传输工具,支持的协议范围更广;wget 是一个下载器,在下载方面的默认设置更优,并具备独特的递归下载能力。大多数用户同时拥有这两个工具会更方便。
curl 能像 wget 那样进行递归下载吗?
不能。curl 仅根据您提供的 URL 进行抓取,不会解析 HTML 或发现链接。递归下载和网站镜像功能仅 wget 具备,而 curl 的维护者本人也将其视为 wget 的主要优势。
为什么 curl 不跟随重定向?
这是设计使然。curl 只报告服务器的响应,而非发起您未请求的请求。若需跟随重定向,请添加 -L。wget 默认会跟随重定向,因为其目标是获取文件,而文件已移动。
curl 和 wget 哪个更快?
对于单次传输,两者速度差异微乎其微——都受网络速度限制。curl 可以通过 -Z 参数并行执行多个传输,这在获取大量小型资源时能显著提升速度。
wget 是否支持 SOCKS 代理?
不原生支持。curl 支持 SOCKS4、SOCKS5 和 HTTPS 代理;wget 则使用标准的 HTTP 代理环境变量。若要在 wget 中使用 SOCKS,需要借助 proxychains 等包装工具或本地桥接工具。
在脚本中应该使用哪个?
两者皆可,但需显式指定选项。对于 API 以及需要检查响应的情况,请使用 curl -sSfL --max-time 30;对于获取文件,请使用 wget --tries=3 --timeout=30。在自动化场景中,请勿依赖这两款工具的默认设置。
curl 或 wget 是否默认安装?
macOS 以及 Windows 10 和 11 系统默认预装了 curl。wget 是大多数 Linux 发行版的标准组件,但在 macOS 和 Windows 上通常未安装。对于跨平台脚本,假设使用 curl 更为稳妥。
总结
尽管人们普遍认为这两者的比较结果难以分晓,但实际上结果比想象中更快得出,因为这两款工具是围绕不同的动词构建的。
curl 用于传输,wget 用于下载。 curl 像 cat 那样写入标准输出,只执行被要求的功能,其余部分则予以报告——这就是它不跟随重定向、不重试,且需要通过参数指定才能写入文件的原因。wget 像 cp 那样写入磁盘,它默认的所有行为都源于最终目标是生成一个实际存在的文件。
有四项功能直接决定了选择,如果您的任务涉及其中任何一项,其余的比较就无关紧要了。 递归下载和网站镜像仅 wget 支持——curl 的维护者本人将其称为 wget 的主要优势,而 curl 没有相应的功能。上传、SOCKS 代理和并行传输仅 curl 支持。
除此之外,两者均可正常工作,实际差异仅在于参数写法和默认设置。在两者之间转换命令时,请注意-O的冲突,因为它在两者中表示截然相反的含义。在任何自动化场景中,请明确表达你的意图,而不是依赖任一工具的默认假设——curl -sSfL --max-time 30和wget --tries=3 --timeout=30这两条命令,即使在明年被其他人阅读时,依然能被理解。
关于代理:我们虽然销售代理服务,但大多数情况下使用这些工具并不需要代理。若确实需要代理,curl 的支持范围更广,而且比工具本身更重要的是,应使用 socks5h:// 而不是 socks5://,这样你的主机名解析路径将与流量传输路径保持一致。
老实说,建议两者都安装。它们体积小,又是免费的,而且关于哪个更好的争论早已尘埃落定——毕竟大多数正在运行的机器上都同时安装了这两个工具。