在两个分开的变量中获取卷曲响应的标题和正文?

Dan*_*iel 10 bash curl

我正在寻找一种方法来进行一次卷曲调用并从中获取变量:一个带有标题,另一个带有响应体.

我发现了几个问题,询问如何将标题与正文分开,但人们似乎只对其中一个感兴趣.我需要标题和正文.

我不能使用外部文件来存储正文(因此使用-o $文件不是一个选项).

我可以用

headers=$(curl -D /dev/stdout $URL)
Run Code Online (Sandbox Code Playgroud)

将标题放入一个变量,但如何将输出重定向到另一个变量?

非常感谢!

0x1*_*040 10

我想分享一种解决卷曲响应的方法,无需任何外部程序,仅限bash.

首先,获得卷曲请求传递的响应-sw "%{http_code}".

res=$(curl -sw "%{http_code}" $url)
Run Code Online (Sandbox Code Playgroud)

结果将是一个包含正文后跟http代码的字符串.

然后,获取http代码:

http_code="${res:${#res}-3}"
Run Code Online (Sandbox Code Playgroud)

和身体:

if [ ${#res} -eq 3 ]; then
  body=""
else
  body="${res:0:${#res}-3}"
fi
Run Code Online (Sandbox Code Playgroud)

请注意,如果http_code和响应的长度相等(长度为3),则body为空.否则,只需删除http代码即可获得正文.

  • 这是一种仅检查status_code的更简单方法.然而最初的问题是关于解析头字段,我喜欢这个解决方案并在我的库中使用它.(https://bitbucket.org/kisp/bash_lib/src/ccd542aa361bf66458195d71a93aeb475100c832/http_get.sh?fileviewer=file-view-default)感谢您的提示. (2认同)

gle*_*man 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[@]}"
Run Code Online (Sandbox Code Playgroud)

要将数组的元素连接到单个标量变量:

the_body=$( IFS=$'\n'; echo "$body[*]" )
Run Code Online (Sandbox Code Playgroud)

bash4.3中,您可以使用命名引用来简化从"标题"模式切换到"正文"模式:

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")
Run Code Online (Sandbox Code Playgroud)

出于某种原因,格伦杰克曼的回答没有抓住身体部分的反应.我不得不将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")
Run Code Online (Sandbox Code Playgroud)

谢谢你,格伦!


Chr*_*son 5

0x10203040 的回答的启发,以下脚本将响应标头放入一个变量中,将响应正文放入另一个变量中。我对此很菜鸟,所以我可能做了一些不明智/低效的事情;请随时提出改进建议。

\n
# Perform the request:\n# - Optionally suppress progress output from the terminal (-s switch).\n# - Include the response headers in the output (-i switch).\n# - Append the response header/body sizes to the output (-w argument).\nURL="https://example.com/"\nresponse=$(curl -si -w "\\n%{size_header},%{size_download}" "${URL}")\n\n# Extract the response header size.\nheaderSize=$(sed -n \'$ s/^\\([0-9]*\\),.*$/\\1/ p\' <<< "${response}")\n\n# Extract the response body size.\nbodySize=$(sed -n \'$ s/^.*,\\([0-9]*\\)$/\\1/ p\' <<< "${response}")\n\n# Extract the response headers.\nheaders="${response:0:${headerSize}}"\n\n# Extract the response body.\nbody="${response:${headerSize}:${bodySize}}"\n
Run Code Online (Sandbox Code Playgroud)\n

解释

\n

curl\xe2\x80\x93 传输 URL

\n

--include

\n

使用该--include (-i)选项将响应标头包含在curl\ 的标准输出中,位于响应正文之前。

\n

--write-out

\n

使用该--write-out (-w)选项将一些有用的内容附加到curl\ 的标准输出的末尾:

\n
    \n
  • \\n(一个新行,因此sed可以单独处理标题/正文尺寸)
  • \n
  • %{size_header},%{size_download}(分别是响应头大小和响应正文大小,用逗号或其他东西分隔)
  • \n
\n
... -w "\\n%{size_header},%{size_download}" ...\n
Run Code Online (Sandbox Code Playgroud)\n

--silent(选修的)

\n

根据请求的类型,curl可能会将进度更新输出到终端中的 stderr。它不会影响标准输出,但您可以使用该--silent (-s)选项抑制它。(浏览此处获取更多信息。)

\n
\n

使用命令替换( $(...)) 在子 shell 中执行curl,并将其整个 stdout(响应标头和正文)$response暂时放入单个变量中;事后我们将分别从中提取标题和正文。

\n
response=$(curl -si -w "\\n%{size_header},%{size_download}" "${URL}")\n
Run Code Online (Sandbox Code Playgroud)\n

$response应该包含这样的内容:

\n
HTTP/2 200 \nage: 384681\ncache-control: max-age=604800\ncontent-type: text/html; charset=UTF-8\ndate: Tue, 03 Nov 2020 06:54:45 GMT\netag: "3147526947+ident"\nexpires: Tue, 10 Nov 2020 06:54:45 GMT\nlast-modified: Thu, 17 Oct 2019 07:18:26 GMT\nserver: ECS (ord/4CB8)\nvary: Accept-Encoding\nx-cache: HIT\ncontent-length: 1256\n\n<!doctype html>\n<html>\n...\n</html>\n\n331,1256\n
Run Code Online (Sandbox Code Playgroud)\n

由于该选项,请注意最后一行的标题、主体尺寸-w

\n

sed\xe2\x80\x93 流编辑器

\n
HTTP/2 200 \nage: 384681\ncache-control: max-age=604800\ncontent-type: text/html; charset=UTF-8\ndate: Tue, 03 Nov 2020 06:54:45 GMT\netag: "3147526947+ident"\nexpires: Tue, 10 Nov 2020 06:54:45 GMT\nlast-modified: Thu, 17 Oct 2019 07:18:26 GMT\nserver: ECS (ord/4CB8)\nvary: Accept-Encoding\nx-cache: HIT\ncontent-length: 1256\n\n<!doctype html>\n<html>\n...\n</html>\n\n331,1256\n
Run Code Online (Sandbox Code Playgroud)\n

--silent

\n

使用--silent( --quiet/ -n) 选项可以防止sed输出经过它的每一行。

\n

$地址

\n

使用$地址仅处理最后一行(标题/正文大小)。

\n

s命令

\n

使用s命令来执行替换。基本上,使用正则表达式来匹配包含标题/正文大小的行,然后仅用标题大小或正文大小替换整行,然后输出替换的行,即仅输出标题大小或仅输出正文大小:

\n
    \n
  1. s:替换命令。
  2. \n
  3. /:开始正则表达式搜索模式。
  4. \n
  5. ^: 匹配行的开头。
  6. \n
  7. \\(:开始一个包含标题大小的组。
  8. \n
  9. [0-9]*:匹配0个或多个数字(即标题大小)。
  10. \n
  11. \\):完成包含标题大小的组。
  12. \n
  13. ,.*:在组之后,匹配一个逗号,后跟 0 个或多个任意字符(以匹配该行的其余部分)。
  14. \n
  15. $: 匹配行尾。
  16. \n
  17. /:完成正则表达式搜索模式;开始替换模式。
  18. \n
  19. \\1:用组(即标题大小)替换匹配的文本(即整行)。
  20. \n
  21. /:结束替换模式。
  22. \n
\n
s/^\\([0-9]*\\),.*$/\\1/\n
Run Code Online (Sandbox Code Playgroud)\n

上面是标题大小,在逗号之前;下面是类似的身体尺寸,在逗号后面。

\n
s/^.*,\\([0-9]*\\)$/\\1/\n
Run Code Online (Sandbox Code Playgroud)\n

p命令

\n

尽管有选项,但使用该p命令输出已处理的行-n

\n
\n

使用“ here-string ”( ) 将\'s<<<传递到\'s stdin。由于我们抑制了\ 的输出 ( ),仅处理最后一行 ( ),用标题大小或正文大小替换 ( ) 整行,然后输出它 ( ),应该分别输出它们。并再次使用命令替换将它们放入和变量中。curl$responsesedsed-n$s/...psed$headerSize$bodySize

\n
... sed -n \'$ s/^\\([0-9]*\\),.*$/\\1/ p\' ...\n... sed -n \'$ s/^.*,\\([0-9]*\\)$/\\1/ p\' ...\n
Run Code Online (Sandbox Code Playgroud)\n
\n

最后,现在知道标头和正文的大小,使用参数子字符串扩展( ${variable:offset:length}) 将响应标头和响应正文拉入单独$headers$body变量中。

\n
s/^\\([0-9]*\\),.*$/\\1/\n
Run Code Online (Sandbox Code Playgroud)\n

故障排除

\n

使用以下命令在 macOS High Sierra 10.13.6 中为我工作:

\n
    \n
  • GNU bash 版本 3.2.57(1)-发布 (x86_64-apple-darwin17)
  • \n
  • 卷曲 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0
  • \n
\n

如果你计算字符数,以下事情让我困惑:

\n
    \n
  • HTTP 标头具有用于 EOL 的 CR+LF,因此每个 EOL 为 2 个字节。
  • \n
  • 某些代码编辑器(例如 Visual Studio Code)会立即标准化行结尾,因此编辑器中的最终结果可能与实际输出不完全相同curl
  • \n
  • 某些字符编码(例如 UTF-8 等)使用多个字节来表示某些单个字符,因此字符数可能少于header/body 的大小(以字节为单位);类似地,对于可能不会出现在文本/代码编辑器中的控制字符/其他非打印字符等。
  • \n
\n

因此,最好使用十六进制编辑器而不是文本编辑器来进行故障排除。

\n