Shell脚本本地可见响应但curl客户端收到空回复求助
curl请求Shell简易HTTP服务器返回空响应的排查与解决
我编写了一个Shell脚本实现简易HTTP服务器,调用天气API返回目标城市的湿度和温度。运行脚本后,本地控制台能输出预期响应内容,但执行curl localhost:8080/xyz时收到错误:
curl: (52) Empty reply from server
脚本代码如下:
#!/bin/sh PORT="${PORT:-8080}" AUTOR="" echo "% $(date) -- $AUTOR -- $PORT %" KEY= while true; do nc -l -p "$PORT" | ( read -r request_line || exit 1 CITY_VAL=$(echo "$request_line" | awk '{print substr($2, 2)}') while read -r header && [ "$header" != "" ] && [ "$header" != "$(printf '\r')" ]; do : done URL="http://api.weatherapi.com/v1/current.json?key=$KEY&q=$CITY_VAL&aqi=no" response=$(wget -q -O - "$URL") temp=$(echo "$response" | grep -o '"temp_c":[^,]*' | sed 's/"temp_c"://') humidity=$(echo "$response" | grep -o '"humidity":[^,]*' | sed 's/"humidity"://') { printf "HTTP/1.1 200 OK\r\n" printf "Content-Type: text/plain\r\n" printf "Content-Length: %s\r\n" "$(expr 16 + ${#humidity} + ${#temp})" printf "Connection: close\r\n" printf "\r\n" printf "Wilgotność %s temperatura %s\r\n" "$humidity" "$temp" } ) done
本地控制台输出(正常):
./test2.sh % pon, 28 kwi 2025, 21:48:29 CEST -- -- 8080 % HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 22 Connection: close Wilgotność 44 temperatura 12.4
核心原因
脚本的管道方向错误,导致响应未发送给客户端:
nc -l -p "$PORT" | (...)是将nc接收到的客户端请求(nc的stdout)传递给子shell处理;- 子shell生成的响应直接输出到终端stdout,而非回传给nc的连接通道(nc的stdin),因此客户端无法收到任何数据,返回空响应。
解决办法
根据nc版本的不同,有两种可靠的修正方式:
方式1:利用命名管道实现双向通信(兼容所有POSIX环境)
通过命名管道将子shell的响应回传给nc,让nc发送给客户端:
#!/bin/sh PORT="${PORT:-8080}" AUTOR="" echo "% $(date) -- $AUTOR -- $PORT %" KEY= # 创建临时命名管道,脚本退出时自动清理 mkfifo request_pipe response_pipe trap 'rm -f request_pipe response_pipe' EXIT while true; do # nc读取response_pipe的响应发给客户端,将客户端请求写入request_pipe cat response_pipe | nc -l -p "$PORT" > request_pipe & NC_PID=$! # 处理请求并生成响应 ( read -r request_line < request_pipe || exit 1 CITY_VAL=$(echo "$request_line" | awk '{print substr($2, 2)}') while read -r header && [ "$header" != "" ] && [ "$header" != "$(printf '\r')" ]; do : done URL="http://api.weatherapi.com/v1/current.json?key=$KEY&q=$CITY_VAL&aqi=no" response=$(wget -q -O - "$URL") temp=$(echo "$response" | grep -o '"temp_c":[^,]*' | sed 's/"temp_c"://') humidity=$(echo "$response" | grep -o '"humidity":[^,]*' | sed 's/"humidity"://') # 将响应写入response_pipe,由nc发送给客户端 { printf "HTTP/1.1 200 OK\r\n" printf "Content-Type: text/plain\r\n" printf "Content-Length: %s\r\n" "$(expr 16 + ${#humidity} + ${#temp})" printf "Connection: close\r\n" printf "\r\n" printf "Wilgotność %s temperatura %s\r\n" "$humidity" "$temp" } > response_pipe ) wait $NC_PID done
方式2:使用nc的-e选项(仅GNU netcat支持)
如果你的系统安装的是GNU netcat(而非OpenBSD版本),可以直接用-e选项将连接的stdin/stdout绑定到处理逻辑,简化代码:
#!/bin/sh PORT="${PORT:-8080}" AUTOR="" echo "% $(date) -- $AUTOR -- $PORT %" KEY= # 定义处理请求的函数 handle_request() { read -r request_line || exit 1 CITY_VAL=$(echo "$request_line" | awk '{print substr($2, 2)}') while read -r header && [ "$header" != "" ] && [ "$header" != "$(printf '\r')" ]; do : done URL="http://api.weatherapi.com/v1/current.json?key=$KEY&q=$CITY_VAL&aqi=no" response=$(wget -q -O - "$URL") temp=$(echo "$response" | grep -o '"temp_c":[^,]*' | sed 's/"temp_c"://') humidity=$(echo "$response" | grep -o '"humidity":[^,]*' | sed 's/"humidity"://') # 直接输出响应,由nc发送给客户端 { printf "HTTP/1.1 200 OK\r\n" printf "Content-Type: text/plain\r\n" printf "Content-Length: %s\r\n" "$(expr 16 + ${#humidity} + ${#temp})" printf "Connection: close\r\n" printf "\r\n" printf "Wilgotność %s temperatura %s\r\n" "$humidity" "$temp" } } while true; do # -e选项指定处理请求的命令,自动绑定连接的IO nc -l -p "$PORT" -e sh -c 'handle_request' done
内容的提问来源于stack exchange,提问作者Hubert Kiszka
相关产品推荐
相关产品推荐

