Linux常用命令--curl与wget

摘要

  • curl 与 wget 怎么选,以及两边的常用下载、请求、上传写法

  • 重定向、证书校验、失败重试、耗时统计

  • 源码安装 curl,以及 HTTPie(http 命令)的基本用法

  • 未特别标注的 curl 参数,按 CentOS 8 自带的 curl 7.61.1 来写

curl 和 wget 怎么选

curl 默认把响应打到标准输出,适合看接口、接管道、写脚本。wget 默认按 URL 里的文件名存到磁盘,适合下载文件和递归镜像。

curl wget
默认行为 响应打到标准输出 保存为文件
整站抓取 不做网站镜像 --mirror / -r 递归下载
请求构造 方法、Header、JSON、上传都直接支持 以 GET 下载为主,也能 POST、加 Header
失败与重试 --retry、-f、-w 适合写进脚本 -c 断点续传、-t 重试次数
常见环境 Linux、macOS 大多自带 Linux 大多自带,macOS 通常要另装

CentOS Linux 8 已于 2021-12-31 停止维护,默认的 yum 源已经迁入归档。还在用这套系统时,可以临时把系统源换成 https://vault.centos.org 后再执行文中的 yum。

Vault 只是冻结的历史软件包归档,不再提供安全更新,不能视为恢复了系统维护。生产环境应尽快迁移到仍受支持的 Rocky Linux、AlmaLinux、RHEL 或其他系统。EPEL 8 本身仍跟随 RHEL 8 生命周期维护到 2029-05-31,但它依赖的 CentOS Linux 8 基础仓库仍可能因为系统停止维护而安装失败。

curl

版本提示

本文以 CentOS 8 自带的 curl 7.61.1 为基础,不同版本的常见差异如下:

  • 7.66.0:增加 --parallel 并行传输

  • 7.67.0:HTTP 429 开始触发 --retry

  • 7.73.0:增加 --output-dir,curl --help 改为只显示常用参数,完整参数使用 curl --help all

  • 7.76.0:增加 --fail-with-body

  • 7.82.0:增加 --json

  • 8.2.0:增加 --ca-native、--proxy-ca-native,可以使用操作系统原生 CA 存储

  • 8.3.0:增加命令行变量 --variable 和 --expand-*

  • 8.10.0:增加 --skip-existing,并支持用 curl --help 参数名 查看指定参数的帮助

  • 8.13.0:--url @文件 可以从文件读取 URL 列表

  • 8.16.0:增加 --follow、--out-null、--parallel-max-host,重试时间支持小数,并把默认最低 TLS 版本提升到 1.2

  • 8.19.0:--limit-rate、--max-filesize 支持小数

  • 8.20.0:上游构建默认关闭 NTLM,SMB 改成编译时选择启用,并移除 RTMP

  • 8.21.0:URL glob 支持命名,并增加 HTTP/3 代理 CONNECT、MASQUE CONNECT-UDP

  • 8.22.0:增加实验性的 RFC 9421 HTTP Message Signatures;移除 TLS-SRP,并阻止 SPNEGO 自动回退到 NTLM

截至 2026-09-02,curl 最新稳定版是 8.22.0。先用 curl --version 查看实际版本和编译特性,再决定是否使用较新的参数;发行版自带的 curl 可能会回移安全补丁,但通常不会增加新参数。

查看页面和下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看页面内容,默认 GET,结果打到标准输出
curl url

# -o 指定保存路径,可以带目录
curl -o /path/to/fileName url

# -O 使用 URL 里的文件名,文件出现在当前目录,不能在 -O 后面再写目录
curl -O url

# 默认是进度表(百分比、速度、剩余时间)。-# 才是一条进度条
# -C - 从本地已有文件的末尾继续下载。服务器必须支持 Range,并返回 206
curl -# -C - -o fileName url

# 限速 1MB/s。wget 里对应的参数是 --limit-rate
curl --limit-rate 1m -o fileName url

--output-dir 从 curl 7.73.0 才有。CentOS 8 的 7.61.1 会报未知参数。

1
2
3
4
5
# curl >= 7.73.0,可以指定目录,同时用 -O 保留 URL 中的文件名
curl -L --output-dir /path/to/dir -O https://example.com/file.ext

# curl 7.61.1:先进入目录
cd /path/to/dir && curl -L -O https://example.com/file.ext

重定向

-L 会跟随 301/302/303/307/308。其中 301、302、303 会把 POST 改成 GET;307 和 308 保持原来的方法。

-d 本身就会发 POST。不要再加 -X POST,否则每一次重定向都会被强制成 POST。

1
2
3
4
5
6
7
8
9
10
11
# 跟随重定向。很多下载链接会先返回 302/301
curl -L -O https://example.com/file.ext

# 301/302/303 也继续使用 POST
curl -L --post301 --post302 --post303 -d 'name=zhangsan' http://example.com

# 最多跟随 5 次,避免重定向循环
curl -L --max-redirs 5 http://example.com

# -I 只取响应头,用来看完整跳转链路
curl -IL http://example.com

指定方法和请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# JSON 要自己设置 Content-Type。-d 和 --data-binary 默认都按表单类型发送
# 命令行里直接给 -d 的内容会原样发送;使用 -d @文件 时,文件中的回车、换行和 NUL 会被移除
# --data-binary 按原样发送正文,包括文件中的回车和换行。两者在数据以 @ 开头时,都把后面当成文件名
curl url \
-H "Content-Type: application/json" \
--data-binary '{"data":"content"}'

# JSON 放在文件里
curl url \
-H "Content-Type: application/json" \
--data-binary @body.json

# 表单。-d 默认的 Content-Type 就是 application/x-www-form-urlencoded
curl url \
-d "name=zhangsan&age=30"

# --data-urlencode 会先做 URL 编码,再放进 POST 请求体。中文和空格要用它
curl --data-urlencode 'name=张三' url

# -G 把 -d / --data-urlencode 的内容附到 URL 查询串上,方法仍是 GET
# 实际请求:url?name=%E5%BC%A0%E4%B8%89&age=30
curl -G --data-urlencode 'name=张三' -d 'age=30' url

curl 7.82.0 起可以用 --json,它会设置 Content-Type: application/json、Accept: application/json 并按 POST 发送。7.61.1 没有这个参数。

1
curl --json '{"name":"zhangsan"}' url

上传文件

-F 会自动加上带 boundary 的 Content-Type: multipart/form-data。再手写一个不带 boundary 的 Content-Type,服务端经常解析失败。

1
2
3
4
5
6
7
8
9
10
11
# @ 后面是本地路径。type 是文件的 MIME,filename 是服务端看到的文件名
# 省略 type 时,curl 先根据文件扩展名推断 MIME,无法识别时使用 application/octet-stream
# 省略 filename 时,服务端看到的是本地文件名
curl -F 'file=@photo.png;type=image/png;filename=me.png' url

# 一个字段一个 -F
curl -F 'file=@photo.png;type=image/png;filename=me.png' -F 'name=java' url

# 同一个 multipart 里再带一段 JSON
curl -F 'file=@photo.png;type=image/png;filename=me.png' \
-F 'jsonBody={"name":"java"};type=application/json' url

认证、Cookie、代理和调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# HTTP Basic。只写用户名时 curl 会交互式询问密码,避免密码进入 shell 历史
curl -u user url

# Bearer Token
curl -H "Authorization: Bearer token" url

# -c 把服务器返回的 Cookie 存进文件,-b 在下次请求里带上
curl -c cookies.txt url
curl -b cookies.txt url

# 声明接受压缩响应,并自动解压
curl --compressed url

# 走代理
curl -x http://127.0.0.1:8080 url

# 打印请求头、响应头和 TLS 握手
curl -v url

# --connect-timeout 只限制建立连接;-m 限制这一次传输的总时间,含下载过程
curl --connect-timeout 5 -m 30 url

更多实用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 把响应头和正文一起输出到标准输出
# 下载二进制文件时不要用 -i,否则响应头也会混进输出
curl -i https://example.com

# -D 单独保存响应头,-o 单独保存正文
curl -D headers.txt -o body.bin https://example.com/file

# 只请求前 1 MiB。真正的范围下载要求服务器返回 206
# 如果服务器忽略 Range 并返回 200,得到的可能是完整文件
curl -r 0-1048575 -o first-1m.bin https://example.com/file.zip

# -O 保存文件,-J 改用 Content-Disposition 中的文件名
# 文件名由服务器决定,只应对可信服务器使用
curl -L -OJ https://example.com/download

# 不修改系统 DNS,临时把域名和端口解析到指定 IP
# URL 中仍使用原域名,因此 Host 和 TLS SNI 不会改变
curl --resolve example.com:443:192.0.2.10 https://example.com/

# 使用 PEM 格式的客户端证书和私钥进行双向 TLS 认证
# 私钥文件应限制读取权限
curl --cert client.crt --key client.key https://example.com/private

# 通过 Unix Socket 请求 Docker API。执行用户需要有 socket 访问权限
curl --unix-socket /var/run/docker.sock http://localhost/version

# 强制使用 IPv4 或 IPv6
curl -4 https://example.com
curl -6 https://example.com

# 指定 HTTP 协议版本。HTTP/2 还要求当前 curl 的构建包含 HTTP2 特性
curl --http1.1 https://example.com
curl --http2 https://example.com

# curl >= 7.66.0:并行下载多个文件,并分别使用 URL 中的文件名
curl --parallel --remote-name-all \
https://example.com/a.zip \
https://example.com/b.zip

可以通过 curl --version 的 Features 行确认 HTTP/2 等编译特性。CentOS 8 自带的 curl 7.61.1 不支持 --parallel。

curl 8.x 新功能示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# curl >= 8.2.0:使用操作系统原生 CA 存储
# 是否可用取决于操作系统、TLS 后端和 curl 的编译方式
curl --ca-native https://example.com

# curl >= 8.3.0:从环境变量导入 Token,再展开到请求头
# 命令行参数中不会直接出现 Token 的值
curl --variable '%API_TOKEN' \
--expand-header 'Authorization: Bearer {{API_TOKEN}}' \
https://api.example.com/users

# 变量展开时可以做 URL 编码
curl --variable 'q=中文 空格' \
--expand-url 'https://example.com/search?q={{q:url}}'

# curl >= 8.10.0:目标文件已经存在时直接跳过,不覆盖也不重新下载
curl --skip-existing -O https://example.com/file.zip

# curl >= 8.13.0:urls.txt 每行一个 URL,空行和以 # 开头的行会被忽略
# 从文件读取 URL 时,每个 URL 都隐含 -O;这里同时并行下载并跳过已有文件
curl --url @urls.txt --parallel --skip-existing

# curl >= 8.16.0:按 HTTP 规范处理自定义方法的重定向
# 301/302/303 可以改成 GET,307/308 保持 PATCH;这和 -L 强制保留 -X 方法不同
curl --follow -X PATCH \
-H "Content-Type: application/json" \
--data-binary @body.json \
https://example.com/resource

# 丢弃响应正文,比写死 /dev/null 更便于跨平台使用
curl --out-null -w "HTTP %{http_code}\n" https://example.com/health

# 并行下载,但同一个主机最多同时建立 2 个传输
curl --parallel --parallel-max-host 2 --url @urls.txt

# curl >= 8.16.0:重试延时和重试总时间可以使用小数
curl --retry 3 --retry-delay 0.5 --retry-max-time 5.5 https://example.com

# curl >= 8.19.0:限速值可以使用小数
curl --limit-rate 1.5M -O https://example.com/file.zip

curl 8.22.0 的 RFC 9421 HTTP Message Signatures 仍是实验功能,必须在编译时显式启用,官方不建议用于生产环境,也不保证实验阶段的参数保持兼容。可以先用 curl --help all 确认当前构建是否包含 --httpsig-*:

1
2
3
4
5
6
7
8
9
10
11
12
# HTTPSIG_KEY 是十六进制编码的 HMAC 共享密钥
export HTTPSIG_KEY='00112233445566778899aabbccddeeff'

curl --variable '%HTTPSIG_KEY' \
--httpsig-algo hmac-sha256 \
--expand-httpsig-key '{{HTTPSIG_KEY}}' \
--httpsig-keyid demo-key \
--httpsig-headers 'method authority path content-type:' \
--json '{"name":"zhangsan"}' \
https://example.com/api

unset HTTPSIG_KEY

FTP

密码写在命令行里时,可能出现在 shell 历史和 ps 输出中。只给用户名,让 curl 交互式询问密码:

1
2
3
4
5
# -O 把内容存成 URL 最后一段的文件名
curl --user user -O ftp://ip:port/file

# -T 上传本地文件
curl --user user -T file ftp://ip:port/path/

在脚本里使用

1
curl -fsSL url

这条命令把正文打到标准输出。要落盘时再加上 -o。

  • -s 关掉进度和报错,-S 在静默时仍把错误打出来。两个一起用,脚本输出里只留下真正的失败信息。

  • -f 遇到 404、500 这类 HTTP 错误时,不把错误页正文打出来,并且以非 0 状态退出。脚本可以直接看 $?。

  • -L 跟随重定向。

-f 并非绝对可靠,认证过程中的 401、407 等状态可能有例外。curl 7.76.0 起,如果既要非 0 退出状态又要保留错误响应正文,可以使用 --fail-with-body。

证书校验失败

curl: (60) SSL certificate problem: certificate has expired 常见有三类原因。

先看本机时间

HTTPS 会检查证书有效期。本机时间快了或慢了,也会报证书过期,或者 certificate is not yet valid。先执行 date 看时间是否正确。

网站证书过期或证书链不完整

站点证书需要在服务器上更新。一时改不了、又确认访问目标没有问题时,可以临时跳过校验:

1
2
3
curl -k https://www.example.com
# -k 等同于 --insecure。-v 仍会打印告警,便于确认是校验被跳过了
curl -k -v https://www.example.com

-k 只适合临时排查。跳过校验之后无法发现中间人替换证书,传输口令或 Token 的脚本里不要加它。

系统根证书过期

curl -v 输出里的 CAfile: 是这一次实际使用的证书包。

1
curl -v https://www.example.com

curl-config --ca 打印的是编译 curl 时写死的默认路径。在 CentOS 8 中,这个命令由 libcurl-devel 软件包提供:

1
2
3
yum install libcurl-devel
curl-config --ca
curl-config --version

系统自带的证书包优先用软件源更新。CentOS 8 的源如果已经失效,下面这条会失败,需要先换成 vault 源。

1
yum update -y ca-certificates

不要直接覆盖 CAfile 指向的系统证书包。CentOS 的证书包由 update-ca-trust 生成,手动修改会在下次更新时丢失,也可能影响其他程序。

源不可用、又必须临时使用新证书包时,可以从 CA Extract 下载 cacert.pem,只对当前命令指定:

1
curl --cacert /path/to/cacert.pem https://www.example.com

也可以临时设置 CURL_CA_BUNDLE=/path/to/cacert.pem。如果是给系统增加自建 CA,应把 CA 证书放进 /etc/pki/ca-trust/source/anchors/,再执行 update-ca-trust extract,而不是修改生成后的 bundle。

按状态码判断失败,以及统计耗时

-I 发的是 HEAD。有的服务对 HEAD 和 GET 返回不同状态码,检查页面是否可用时用 GET,也就是不要加 -I。

--retry 只对超时和部分临时错误重试。curl 7.61.1 支持的 HTTP 状态包括 408、500、502、503、504;HTTP 429 从 7.67.0 起才会触发重试。连接被拒绝要另加 --retry-connrefused。

-m 是单次传输的上限。--retry-max-time 会在开始下一次重试前检查累计时间,但不会中断已经开始的那一次请求,因此它不是严格的整体超时;实际总耗时可能超过设置值。需要严格控制脚本总运行时间时,还应由外层的 timeout 或任务调度器限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

# --retry 3:上述临时错误再试 3 次
# --retry-connrefused:连接被拒绝也重试
# --retry-max-time 30:累计到 30 秒后不再开始下一次重试,但已开始的请求可能继续
# --connect-timeout 5:5 秒内连不上就失败
# -m 10:单次传输最多 10 秒
# -s -S:不显示进度,但保留错误信息
# -o /dev/null:丢掉正文。-w 的结果仍然在标准输出上
# 连接失败时 %{http_code} 是 000
curl_output=$(curl --retry 3 --retry-connrefused --retry-max-time 30 \
--connect-timeout 5 -m 10 -s -S -o /dev/null -w "%{http_code}" \
http://localhost:8080/test.html)

exit_status=$?

if [ "$exit_status" -ne 0 ] || [ "$curl_output" -ne 200 ]; then
current_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "curl 失败或状态码不是 200(exit=${exit_status},http=${curl_output}),当前时间:${current_time}"
fi

-w 还可以输出这些字段:

  • %{time_namelookup}、%{time_connect}、%{time_appconnect}、%{time_pretransfer}、%{time_starttransfer}、%{time_total}:见下面的计时说明,都是从请求开始算的累计时间

  • %{size_download}、%{size_upload}:下载、上传的字节数

  • %{url_effective}:跟随重定向之后的最终 URL

  • %{content_type}:响应的 Content-Type

  • %{remote_ip}:连上的服务器 IP

下面这组时间是累计值。time_connect 里已经包含 DNS,time_starttransfer 里已经包含连接、握手和等到首字节的全部时间。

1
2
3
4
5
6
7
8
curl -o /dev/null -s -w \
"DNS 解析完成: %{time_namelookup}s\n\
TCP 连接完成: %{time_connect}s\n\
TLS 握手完成: %{time_appconnect}s\n\
即将发送请求: %{time_pretransfer}s\n\
收到首字节: %{time_starttransfer}s\n\
请求结束: %{time_total}s\n" \
https://example.com
1
2
3
4
5
6
DNS 解析完成: 0.002119s
TCP 连接完成: 0.002488s
TLS 握手完成: 0.012045s
即将发送请求: 0.012156s
收到首字节: 0.012175s
请求结束: 2.376437s
字段 含义(从请求开始累计)
%{time_namelookup} DNS 解析完成
%{time_connect} TCP 连接完成,已包含 DNS
%{time_appconnect} TLS 握手完成,已包含 DNS 和 TCP。纯 HTTP 时为 0
%{time_pretransfer} 即将把请求发给服务器
%{time_starttransfer} 收到响应的第一个字节
%{time_total} 本次传输结束

某一段花了多久,用相邻两项相减:

  • DNS = time_namelookup

  • TCP = time_connect - time_namelookup

  • TLS = time_appconnect - time_connect(纯 HTTP 时 time_appconnect 为 0,这一项跳过)

  • 请求发出到首字节 = time_starttransfer - time_pretransfer。有请求体时,这段还包含上传请求体的时间,不能全部视为服务端处理耗时

  • 传输正文 = time_total - time_starttransfer

按上面的样例,正文传输大约是 2.364 秒,等到首字节几乎可以忽略。

源码安装

有的系统没装 curl:yum install curl -y。需要较新版本时再从源码装。不要复制旧文章里的固定版本号:旧版 curl 可能包含已经公开的安全漏洞。安装前到 curl releases 选择当前版本,并按 curl 签名说明 校验下载文件的签名或摘要。

装到 /usr/local 后,/usr/local/bin/curl 会和系统的 /usr/bin/curl 并存。动态库版本对不上时,会出现 error while loading shared libraries。只是临时试用时,把下面的安装前缀改成 $HOME/.local,避免碰系统自带的 curl。

编译并安装到 /usr/local

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# gcc/make 用于编译,openssl-devel 提供 TLS,zlib-devel 提供压缩
yum install gcc make cmake openssl openssl-devel zlib-devel pkgconfig

# 截至 2026-09-02 的最新稳定版;安装时仍应到 releases 页面确认
CURL_VERSION='8.22.0'
CURL_TAG="curl-${CURL_VERSION//./_}"

wget "https://github.com/curl/curl/releases/download/${CURL_TAG}/curl-${CURL_VERSION}.tar.gz"
# 解压前,按 curl 官方签名说明校验这个文件
tar -zxf "curl-${CURL_VERSION}.tar.gz"

cd "curl-${CURL_VERSION}"
mkdir build
cd build

# 生成 Makefile,并安装到 /usr/local
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
make && make install

# /usr/local/lib 或 /usr/local/lib64 需要已经写在 /etc/ld.so.conf.d/ 里
ldconfig
hash -r
which curl
curl --version

7.73.0 起,curl --help 只列出常用参数,完整列表是 curl --help all。CentOS 8 自带的 7.61.1 没有分类帮助,curl --help 就是完整列表;在这个版本上执行 curl --help all 会把 all 当成 URL。完整说明用 man curl。

wget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 下载到当前目录,文件名取 URL 的最后一段。curl 默认打到标准输出,wget 默认存文件
wget url

# 保存为指定文件名
wget -O saveFile url

# 保存到指定目录,文件名仍取自 URL
wget -P saveDir url

# 把 file.txt 里的每一行当成一个 URL 来下载
wget -i file.txt

# 断点续传,最多尝试 5 次(包含第一次请求,不是额外重试 5 次)
# 真正续传要求服务器支持 Range 并返回 206;不支持时,wget 可能从头下载并覆盖已有文件
wget -c -t 5 url

# 递归下载,深度最多 5 层
wget -r -l 5 url

# 本地已有同名文件就跳过;各阶段超时 5 秒
wget -nc -T 5 url

# 限速 1MB/s。curl 对应参数是 --limit-rate
wget --limit-rate=1m url

# FTP。下面只适合演示,密码可能出现在 shell 历史和进程列表里
# 自动化脚本更适合使用权限设为 600 的 .netrc
wget --ftp-user=USERNAME --ftp-password=PASSWORD ftp://ip/file

# 确认目标可信、只是证书校验过不去时,临时跳过校验。不要用在会传输口令的脚本里
wget --no-check-certificate https://example.com

更多实用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -O - 把正文输出到标准输出,-q 关闭 wget 自身的日志,适合接管道
# 脚本需要同时检查 wget 和 jq 是否成功时,应先启用 set -o pipefail
wget -qO- https://example.com/api | jq .

# 检查资源是否存在,不保存正文;-S 同时打印服务器响应头
wget --spider -S https://example.com/file.zip

# 根据服务器的 Last-Modified 和本地文件时间决定是否重新下载
# -N 不要和 -O 一起使用,否则无法正常按本地文件时间判断
wget -N https://example.com/file.zip

# 转入后台下载,并把日志写入 download.log
wget -b -o download.log https://example.com/big-file.zip

# HTTP Basic 认证。只写用户名,密码由终端交互式读取
wget --user=username --ask-password https://example.com/private/file.zip

# 仅对这一条命令使用 HTTPS 代理
https_proxy=http://127.0.0.1:8080 \
wget https://example.com/file.zip

# 正常下载文件,同时打印服务器响应头,适合排查重定向和缓存问题
wget -S https://example.com/file.zip

POST、Header 和 Cookie

wget 多数时候用来做 GET 下载,也可以带请求体和 Header。

1
2
3
4
5
6
7
8
9
10
11
# 这部分作为 POST 请求体
wget --post-data 'key1=value1&key2=value2' http://example.com/resource

wget --header="Accept-Encoding: gzip" --header="User-Agent: My-App-Name/0.0.1" http://example.com

# 多个 Cookie 写在同一个头里,用分号隔开
wget --header="Cookie: name1=value1; name2=value2" http://example.com

# cookies.txt 使用 Netscape/Mozilla cookie 文件格式
# Chrome 插件 Get cookies.txt LOCALLY 可以导出这种文件
wget --load-cookies cookies.txt http://example.com

镜像整个网站

1
wget --mirror --convert-links --adjust-extension --page-requisites --no-parent http://example.com
  • --mirror:等同于 -r -N -l inf --no-remove-listing。也就是递归下载、按时间戳跳过没有更新的文件、不限制深度,并保留 FTP 目录列表生成的 .listing 文件。-l inf 表示深度没有上限。

  • --convert-links:下载完成后,把页面里的链接改成本地相对路径,方便离线打开。

  • --adjust-extension:服务器返回的是 HTML,但 URL 没有 .html 时,补上合适的扩展名。

  • --page-requisites:下载页面显示要用到的图片、CSS 等。默认不跨主机,其他域名上的资源要再加 -H(--span-hosts)。

  • --no-parent:不访问起始 URL 所在目录的上级目录。

递归会连续请求。wget 默认遵守 robots.txt。加上请求间隔,避免把对方站点打满:

1
2
wget --mirror --wait=1 --random-wait --convert-links --adjust-extension \
--page-requisites --no-parent http://example.com

--accept / --reject 只决定哪些文件被保存。-m 仍然会遍历站点:

1
2
3
4
# 只保存 rar
wget -m --accept=rar url
# 不保存 gif
wget -m --reject=gif url

还可以限制递归范围和本地目录结构:

1
2
3
4
5
6
7
8
9
# 只跟随 example.com 域名内的链接,并且不访问 /docs/ 的上级目录
wget -r --domains=example.com --no-parent https://example.com/docs/

# 不创建目录层级,所有文件都保存到当前目录
# 不同目录下的同名文件可能发生文件名冲突
wget -r -nd https://example.com/files/

# 正则表达式针对完整 URL,只接受指定路径下的 png 和 jpg
wget -r --accept-regex='/images/.*\.(png|jpg)$' https://example.com/

--accept-regex / --reject-regex 依赖 wget 的正则支持,可以先执行 wget --help 确认当前版本是否提供这些参数。

http 命令是什么(HTTPie 简介)

HTTPie 是一个面向人的命令行 HTTP 客户端,用来调试 API。安装后有两个命令:http 发请求,https 强制走 HTTPS。

和 curl 相比,它的输出带颜色、语法更接近自然语言,并且会自动处理 JSON 的 Content-Type 和格式化。curl 仍然更适合写进脚本,也更常见于服务器上。

1
2
3
4
# CentOS 上通常先启用 EPEL。EPEL 8 仍跟随 RHEL 8 生命周期维护,
# 但 CentOS Linux 8 的基础系统源已经归档,安装失败时见上文的 vault 说明
sudo yum install epel-release -y
sudo yum install httpie -y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
http GET https://api.github.com

# 有请求体时默认 POST。= 是字符串,:= 保留 JSON 类型
# 这条请求的正文是 {"name": "han", "age": 18},age 是数字
# HTTPie 会自动加上 Content-Type: application/json
http POST https://httpbin.org/post name=han age:=18

# 没有请求体时默认 GET。需要 POST 时把方法写出来
# == 是 URL 查询参数,和 JSON 字段的 = 不是一回事
http https://api.example.com/users page==2 size==10

# -f 发送表单。真实密码不要直接写进会保存的 shell 历史
http -f POST https://example.com/login username=admin password=PASSWORD
http -f POST https://example.com/upload file@/path/to/file.txt

# Header 写成 名字:值
http Authorization:"Bearer 123456" https://example.com

# 重定向到文件时,默认只输出原始 body,不做着色和格式化
http https://example.com/file.zip > file.zip

# -d 是下载模式:body 存成文件,终端上显示响应头和进度
# 用 -o 指定文件名。-d 的标准输出不是文件内容,不要再重定向到 zip
http -d -o file.zip https://example.com/file.zip

# 断点续传。file.zip 必须是上一次未下载完的文件
# -c 必须和 -d、-o 一起使用;服务器需要支持 Range 并返回 206
# 如果服务器不支持续传,HTTPie 会重新下载整个文件
http -d -c -o file.zip https://example.com/file.zip

# 跟随重定向。下载模式 -d 已经隐含 --follow
http --follow https://example.com

# 打印完整的请求和响应,适合调试
http -v https://example.com

# HTTP Basic 认证。只写用户名时,会安全地交互式询问密码
http -a username https://example.com/private

# 会话会在同一主机的后续请求中保留 Cookie、认证信息和自定义 Header
http --session=my-session POST https://example.com/login name=user
http --session=my-session https://example.com/profile

# 从文件读取原始 JSON 请求体,内容不会按字段语法重新处理
http POST https://example.com/api \
Content-Type:application/json < body.json

# 只输出响应正文,适合脚本和管道
http -b https://example.com/api

# 只看响应头。-h 在这里是 --headers,不是帮助
http -h GET https://example.com

http --help

HTTPie 的会话文件会以明文保存认证信息、Cookie 和自定义 Header。不要在会话中保存敏感信息;确实需要时,应保护好 HTTPie 配置目录及会话文件的读取权限。

特性 HTTPie(http) curl
可读性 输出带颜色,默认排版友好 更接近协议本身
JSON 自动设置 Content-Type 并格式化输出 自己设置 Header,并用 --data-binary 发送
请求怎么写 接近 方法 URL 字段=值 由 -H、-d、--data-binary 这些参数拼出来
调试 REST API 交互时少打很多参数 同样能做,命令更长
脚本和服务器 需要单独安装,CentOS 上一般走 EPEL 多数系统已经带有,退出码和 -w 更适合脚本