curl 大多数时候被当成“发 HTTP 请求的小工具”,但真到了服务器环境里,你会发现它更像一把瑞士军刀。
拉配置文件、下构建产物、抓 API 返回、处理带认证的下载、写进自动化脚本,这些事情都能落到它身上。
如果你只是偶尔复制一条 curl URL 命令,通常也够用。但一旦开始把下载动作写进脚本、CI/CD 或运维流程,问题就不再是“能不能下载”,而是“这个下载流程够不够稳”。
这篇文章就专门围绕这个问题展开:如何把 curl 从一次性命令,用成一个可靠的文件下载工作流。
先理解 curl 的默认行为
很多人第一次用 curl 会有点困惑,因为它默认不会“帮你把文件保存下来”,而是把远程内容直接输出到终端。
例如:
curl https://www.digitalocean.com/robots.txt
这条命令的效果不是下载一个文件到当前目录,而是把 robots.txt 的文本内容直接打印到标准输出。
这其实很合理。因为 curl 的设计重点从来不只是下载文件,它本质上是一个“数据传输工具”。
所以在工作流里,你可以先把它当成:
- 一个快速查看远程文本内容的工具
- 一个可以继续接管道的命令
- 一个既能用于文件下载,也能用于 API 调试的基础组件
如果你只是想检查一个远程地址返回了什么,这种默认行为反而很方便。
下载文件时,先分清 -O 和 -o
真正进入下载场景后,最常见的两个参数是:
-O-o
看起来只差一个大小写,但语义完全不一样。
-O:保留远程原始文件名
curl -O https://www.digitalocean.com/robots.txt
这会把远程文件保存为当前目录下的 robots.txt。
适合的场景:
- 你信任远程文件名
- 你想快速下载一个单文件资源
- 你在做一次性的本地测试
不适合的场景:
- 当前目录里可能已经有同名文件
- 你希望给文件起一个更明确的业务名
- 你在自动化流程里要控制输出路径
-o:手动指定本地文件名
curl -o do-bots.txt https://www.digitalocean.com/robots.txt
这时远程文件名是什么已经不重要了,本地统一保存为你指定的名字。
如果你的下载动作会进入脚本、构建流程、定时任务或者部署流水线,我更推荐默认优先使用 -o。
原因很简单:输出文件名应该由你的流程控制,而不是由远端 URL 决定。
处理重定向时,不要忘了 -L
很多下载失败并不是因为文件不存在,而是因为 URL 发生了跳转。
最典型的情况是:
http跳https- 老地址跳新地址
- CDN 或鉴权层返回 301/302/307
你可以先用 -I 看响应头:
curl -I www.digitalocean.com/robots.txt
如果你看到类似:
301 Moved Permanently302 Found307 Temporary RedirectLocation: ...
那就说明你需要跟随跳转。
对应的参数是:
curl -L -O www.digitalocean.com/robots.txt
或者:
curl -L -o robots.txt www.digitalocean.com/robots.txt
很多人在脚本里忘了加 -L,结果下载下来的根本不是目标文件,而是一个跳转响应页,后面一串流程都会跟着错。
如果你面对的 URL 来自网页、下载页、对象存储网关或者会变更的外链,-L 基本应该视作默认选项。
遇到受保护资源时,下载方式就不一样了
不是所有文件都能匿名下载。
很多企业内部场景里,下载动作通常挂在认证之后,比如:
- 需要 Basic Auth
- 需要 Bearer Token
- 需要自定义请求头
基本认证:-u
curl -u username:password -O https://example.com/securefile.zip
这类写法简单直接,但也有明显问题:
如果你把用户名和密码直接写进脚本,风险很大。
更稳的做法是把凭据放到环境变量里:
curl -u "$DOWNLOAD_USER:$DOWNLOAD_PASS" -O https://example.com/securefile.zip
Token 认证:-H
curl -H "Authorization: Bearer $API_TOKEN" \
-O https://api.example.com/protected/data.json
这种方式在 API 场景里更常见。
如果你下载的本质上不是静态文件,而是某个接口导出的 JSON、CSV 或二进制结果,通常都要这么做。
一个经验是:只要下载动作和 API 权限有关,就别把它看成“单纯下载文件”,而应该把它当成一个受控请求。
让下载流程更抗网络波动
真正上线到脚本里后,你最需要考虑的不是“下载成功一次”,而是“网络抽风时还能不能继续”。
这时有三类参数特别关键:
- 超时
- 重试
- 断点续传
1. 超时:别让命令一直卡死
curl --max-time 30 -O https://example.com/file.txt
这条命令表示总耗时超过 30 秒就退出。
它适合:
- CI/CD 环境
- 自动化脚本
- 定时任务
因为这些场景里,最怕的不是失败,而是命令一直挂住,把后面流程全堵死。
2. 重试:把短暂故障熬过去
curl --retry 3 -O https://example.com/file.txt
这会在失败时自动重试 3 次。
适合的典型情况:
- 网络抖动
- CDN 某个节点短暂异常
- 临时性的 5xx
在自动化任务里,这个参数通常值回票价。
很多“偶发失败”的任务,其实重试一两次就过了。
3. 断点续传:大文件下载的基本盘
curl -C - -O https://example.com/largefile.iso
-C - 的意思是:从已有下载进度继续。
这在下面这些场景特别有用:
- 下载大镜像
- 下载模型文件
- 下载数据集
- 网络不稳定
前提是服务器支持范围请求(Range Requests)。
如果服务端不支持,这个参数也救不了你。
用 Shell 脚本把下载动作收进流程里
如果你需要定期拉文件,或者把下载动作接进部署流程,那就不要每次手敲命令了,直接写脚本。
例如:
#!/bin/bash
set -euo pipefail
URL="https://example.com/file.zip"
DEST="/home/user/downloads/file.zip"
curl -L \
--retry 3 \
--max-time 60 \
-o "$DEST" \
"$URL"
这个版本虽然简单,但已经比随手写一条 curl URL 稳定得多。
如果你希望再可靠一点,可以继续加上:
- 下载前先创建目录
- 下载后校验文件大小或哈希
- 失败时打印日志
- 和
cron、CI、systemd timer 结合
我自己的建议是:所有会进入自动化流程的下载动作,都应该至少带上 -L、--retry、--max-time 和明确的 -o 路径。
出问题时怎么排查?
curl 最大的优势之一,就是它非常适合排错。
看响应头:-I
curl -I https://example.com/file.zip
适合快速看:
- 状态码
- 重定向
- 内容类型
- 缓存头
看详细过程:-v
curl -v -O https://example.com/file.zip
这会把连接、TLS 握手、请求头、响应头等过程都打印出来。
如果你遇到这些问题:
- SSL 证书报错
- 请求一直不返回
- 服务端拒绝访问
- 认证头不生效
-v 基本是第一把诊断工具。
curl 和 wget 怎么选?
这个问题几乎每隔一段时间都会出现。
我的实际判断很简单:
更适合用 curl 的情况
- 你不只是下载文件,还要顺手调 API
- 你要带认证头、Token、自定义 Header
- 你会把它嵌进脚本、自动化流程、CI
- 你想要更强的请求控制能力
更适合用 wget 的情况
- 你就是纯下载
- 你想后台跑下载
- 你要做镜像抓取或递归抓站
- 你更在意“下载器体验”而不是“请求工具能力”
换句话说:
wget更像“下载器”curl更像“通用传输工具”
如果你平时既要调接口,又要拉文件,直接把 curl 用熟会更划算。
一套更实用的 curl 下载思路
如果把这篇文章压缩成一套实际可执行的经验,我会建议你按下面这套思路来:
-
先确认目标 URL 是否真的返回你要的东西
用curl -I看状态码、重定向、响应头。 -
永远明确本地输出文件名
能用-o就尽量用-o。 -
面对网页给出的下载链接,默认加
-L
不要赌它不会跳转。 -
进入自动化流程时,默认补上
--retry和--max-time -
下载大文件时优先考虑
-C - -
凭据不要硬编码在脚本里
尽量走环境变量或安全配置注入。
这套做法听起来不复杂,但真落到脚本里,会帮你避开很多低级错误。
写在最后
很多人把 curl 当成“偶尔用一下的命令”,其实它真正的价值,在于能把零散的下载动作组织成稳定流程。
尤其是在服务器、部署和自动化环境里,一个可靠的下载命令往往比一个“能跑通一次”的命令更重要。
DigitalOcean 这篇教程的价值,也正是在这里:它不是只告诉你怎么把文件拉下来,而是把下载这件事拆成了几个真正会在生产场景里遇到的步骤。
如果你之后还会把 curl 用在 API 调试、对象存储下载、构建产物拉取、模型文件同步这些场景里,这套思路会比记住几个单独参数更有用。
关于
关注我获取更多资讯