无 curl 容器中,用 bash /dev/tcp 发起 HTTP 请求的方法与注意事项
用 bash /dev/tcp 发起 HTTP 请求的背景2026 年 6 月 16 日有人需要检查一个容器能否通过 Docker 内部网络访问另一个容器即对共享网络上的服务执行一个简单的 GET /health 请求。最直接的做法是使用 curl http://service:8642/health。但这个应用镜像经过了深度精简既没有 curl 也没有 wget也没有其他可以用来打开套接字的工具。bash 手动编写小型 HTTP 请求的方法实际上bash 可以打开 TCP 套接字能手动编写一个小型 HTTP 请求。打开与主机和端口的连接并写入请求只需要使用现有的 shell 即可exec 3/dev/tcp/service/8642 printf GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n 3 cat 3这里的 service 是要访问的主机名。它必须能够解析并且从运行此命令的地方可以访问所以需要先进行设置比如在配置的 Docker 网络中的容器或服务名称或者任何可解析的 DNS 名称。也可以替换为自己的主机和端口。添加头部信息的请求示例上述代码会打印出完整的响应包括状态行、头部信息、空行和响应体。如果要添加一个头部信息比如 Authorization: Bearer 令牌可以在请求结束的空行之前添加另一个以 \r\n 结尾的行exec 3/dev/tcp/service/8642 printf GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n $API_KEY 3 cat 3/dev/tcp 的原理与特点第一次使用时遇到的问题是/dev/tcp 并不是一个真正的设备文件。磁盘上并没有这样的路径ls /dev/tcp 不会找到任何内容从另一个 shell 执行 cat /dev/tcp/... 会报错。它是 bash 内部处理的一种重定向。根据 [Bash 手册](https://www.gnu.org/software/bash/manual/bash.html#Redirections)“/dev/tcp/host/port - 如果 host 是有效的主机名或互联网地址port 是整数端口号或服务名称bash 会尝试打开相应的 TCP 套接字。”选择这些名称是因为[参考](https://www.gnu.org/software/bash/manual/bash.html#Redirections)没有真正的 Unix 系统有 /dev/tcp 或 /dev/udp 层级结构所以不会产生冲突。Bash 会为进行 DNS 查找和 connect(2) 操作exec 3 会为套接字分配一个文件描述符3可以像操作其他文件一样对其进行读写。使用 bash /dev/tcp 发起请求的注意事项有几点需要注意这不是一个真正的 HTTP 客户端。它不能正确解析 HTTP也不能处理重定向、分块响应、压缩、重试、TLS 或 curl 能默默处理的其他功能。这只是一个快速检查连接性和调试的技巧。Connection: close 头部信息很重要。如果没有它服务器在响应后会保持连接打开这是 HTTP/1.1 的默认行为cat 3 会一直等待永远不会到来的字节。要求服务器关闭连接意味着 cat 会到达文件末尾并返回。使用 timeout 6 bash -c ... 可以应对各种情况。不支持 TLS。/dev/tcp 打开的是一个原始套接字所以这只适用于纯文本 HTTP。对于 https需要使用 openssl s_client到那时可能还是使用合适的工具更好。这是 bash 的特性不是 POSIX 标准。dashDebian 的 /bin/sh和 zsh 没有这个功能所以 #!/bin/sh 脚本不能使用它。需要直接调用 bash。这是一个编译时选项在使用 --enable-net-redirections 构建 bash 时会启用。大多数主流版本都会启用它在基于 Debian 的镜像中使用时没有任何问题但 Debian 曾经有几年默认禁用了这个功能[参考](https://bugs.launchpad.net/ubuntu/source/bash/bug/215034)所以在旧系统或非常精简的系统上最好先检查一下。总结与适用场景在日常工作中curl 仍然是首选工具。但在有意精简、无法安装任何软件的容器中这种方法可以在不添加软件包的情况下快速完成检查。这种方法真的能满足所有需求吗