我正在寻找一种方法来进行一次卷曲调用并从中获取变量:一个带有标题,另一个带有响应体。
我发现了几个问题,询问如何将标题与正文分开,但人们似乎只对其中一个感兴趣。我需要标题和正文。
我无法使用外部文件来存储正文(因此使用-o $文件不是一个选项)。
我可以用
headers=$(curl -D /dev/stdout $URL)
将标题放入一个变量,但如何将输出重定向到另一个变量?
非常感谢!
答案 0 :(得分:8)
我想分享一种解析curl响应的方法,无需任何外部程序,仅限bash。
首先,获取通过-sw "%{http_code}"
的curl请求的响应。
res=$(curl -sw "%{http_code}" $url)
结果将是一个包含正文后跟http代码的字符串。
然后,获取http代码:
http_code="${res:${#res}-3}"
身体:
if [ ${#res} -eq 3 ]; then
body=""
else
body="${res:0:${#res}-3}"
fi
请注意,如果http_code和响应的长度相等(长度为3),则body为空。否则,只需删除http代码即可获得正文。
答案 1 :(得分:7)
head=true
while IFS= read -r line; do
if $head; then
if [[ -z $line ]]; then
head=false
else
headers+=("$line")
fi
else
body+=("$line")
fi
done < <(curl -sD - "$url" | sed 's/\r$//')
printf "%s\n" "${headers[@]}"
echo ===
printf "%s\n" "${body[@]}"
将数组的元素连接到单个标量变量中:
the_body=$( IFS=$'\n'; echo "$body[*]" )
在bash
4.3中,您可以使用命名引用来简化从&#34;标题&#34;模式到&#34;身体&#34;模式:
declare -n section=headers
while IFS= read -r line; do
if [[ $line = $'\r' ]]; then
declare -n section=body
fi
section+=("$line")
done < <(curl -sD - "$url")
出于某种原因,格伦·杰克曼的回答没有抓住身体部分的反应。我不得不将curl请求分成另一个命令扩展,然后用双引号括起来。然后我没有使用数组,只是将值连接到变量。这对我有用:
output=$(curl -si -d "" --request POST https://$url)
head=true
while read -r line; do
if $head; then
if [[ $line = $'\r' ]]; then
head=false
else
header="$header"$'\n'"$line"
fi
else
body="$body"$'\n'"$line"
fi
done < <(echo "$output")
谢谢你,格伦!
答案 2 :(得分:0)
受0x10203040's answer的启发,以下脚本将响应标头放入一个变量,并将响应正文放入另一个变量。我不是这个菜鸟,所以我可能做了某事不明智/低效;随时提出改进建议。
# Perform the request:
# - Optionally suppress progress output from the terminal (-s switch).
# - Include the response headers in the output (-i switch).
# - Append the response header/body sizes to the output (-w argument).
URL="https://example.com/"
response=$(curl -si -w "\n%{size_header},%{size_download}" "${URL}")
# Extract the response header size.
headerSize=$(sed -n '$ s/^\([0-9]*\),.*$/\1/ p' <<< "${response}")
# Extract the response body size.
bodySize=$(sed -n '$ s/^.*,\([0-9]*\)$/\1/ p' <<< "${response}")
# Extract the response headers.
headers="${response:0:${headerSize}}"
# Extract the response body.
body="${response:${headerSize}:${bodySize}}"
curl
–传输网址--include
使用--include (-i)
选项将响应标头包含在curl
的标准输出中,位于响应正文之前。
--write-out
使用--write-out (-w)
选项将一些有用的内容附加到curl
的stdout的末尾:
\n
(新行,因此sed
可以单独处理)%{size_header},%{size_download}
(响应标头大小和响应正文大小,分别用逗号或其他分隔符)... -w "\n%{size_header},%{size_download}" ...
--silent
(可选)根据请求的类型,curl
可能会将进度更新输出到终端中的stderr。它不会影响stdout,但是您可以使用--silent (-s)
选项将其抑制。 (有关更多信息,请参见here。)
使用command substitution($(...)
)在子shell中执行curl
,现在将其stdout放入单个$response
变量中;事实结束后,我们将分别从其中提取标头和正文。
response=$(curl -si -w "\n%{size_header},%{size_download}" "${URL}")
$response
应该包含以下内容:
HTTP/2 200
age: 384681
cache-control: max-age=604800
content-type: text/html; charset=UTF-8
date: Tue, 03 Nov 2020 06:54:45 GMT
etag: "3147526947+ident"
expires: Tue, 10 Nov 2020 06:54:45 GMT
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
server: ECS (ord/4CB8)
vary: Accept-Encoding
x-cache: HIT
content-length: 1256
<!doctype html>
<html>
...
</html>
331,1256
请注意标题,仅在最后一行显示正文大小。
sed
–流编辑器... sed -n '$ s/^\([0-9]*\),.*$/\1/ p' ...
... sed -n '$ s/^.*,\([0-9]*\)$/\1/ p' ...
--silent
使用--silent
(--quiet
)(-n
)选项可防止sed
输出通过它的每一行。
$
address 使用$
地址仅处理最后一行。
s
command 使用s
命令进行替换:
s
:替换命令。/
:开始使用正则表达式搜索模式。^
:匹配行的开头。\(
:开始一个包含报头大小的组。[0-9]*
:匹配0个或多个数字(即标头大小)。\)
:完成包含标头大小的组。,.*
:在该组之后,匹配一个逗号,后跟0个或多个任意字符(以匹配该行的其余部分)。$
:匹配行尾。/
:完成正则表达式搜索模式;开始替换模式。\1
:将匹配的文本(即整行)替换为组(即标题大小)。/
:结束替换模式。s/^\([0-9]*\),.*$/\1/
上面是标题的大小,在逗号之前;下面的体形类似, 后是逗号。
s/^.*,\([0-9]*\)$/\1/
p
command 尽管有p
,也使用-n
命令输出已处理的行。
使用“ here-string”(<<<
)将curl
的{{1}}传递到$response
的标准输入。由于我们取消了sed
的输出(sed
),因此仅处理了最后一行(-n
),然后用标题大小或身体大小,然后输出($
),s/...
应该分别输出那些大小。然后再次使用command substitution将它们放入p
和sed
变量中。
$headerSize
最后,现在知道标头和正文的大小,使用parameter substring expansion($bodySize
)将响应标头和响应正文拉到单独的headerSize=$(sed -n '$ s/^\([0-9]*\),.*$/\1/ p' <<< "${response}")
bodySize=$(sed -n '$ s/^.*,\([0-9]*\)$/\1/ p' <<< "${response}")
和${variable:offset:length}
变量中
$headers
在macOS High Sierra 10.13.6中使用以下命令为我工作:
如果您要数字符,以下几件事使我震惊:
$body
的实际输出不完全相同。